Buildtide
Author: Hussain Mir Ali

I am interested in web and mobile technologies.

If you have any questions or feedback then message me at devtips@Buildtide.com.

Running JavaScript on GPU

GPUs have played crucial role in computer graphics during the last two decades. But recently GPUs have taken over the tasks performed by CPU for compute intensive applications. These applications include data analysis which involves dealing with large multi-dimensional arrays, parallel processing and cryptocurrency mining applications for processing 256-bit hashes. 

To enable faster execution of certain JavaScript code it can be run on the GPU. The gpu.js library makes running JavaScript on GPU convenient for developers.  Fig 1.0 shows the flow for gpu.js library where the JavaScript code is converted to shader language which is then run on GPU. In case there is no GPU then code directly runs on CPU. 




Fig 1.0 Flow Diagram for gpu.js


Installation:

npm:
npm install gpu.js --save
script tag:
The js file can directly be downloaded from GitHub repository and included as static file.
<script src="/path/to/js/gpu.min.js"></script>
Usage:
const gpu = new GPU();
Before using this library in the code a new instance of GPU object can be created for later use.
Basically the code which is to be run on the GPU is put in a 'kernel' function which will return a single output. Additionally multiples out puts can be returned using a 'kernel map' which processes multiple math operations. 

Using 'createKernel' method developers can define the code that is to be run on GPU. The following code is used to multiply a 512 x 512 matrix and get the output.

const gpu = new GPU();
const multiplyMatrix = gpu.createKernel(function(a, b) {
var sum = 0;
for (var i = 0; i < 512; i++) {
sum += a[this.thread.y][i] * b[i][this.thread.x];
}
return sum;
},{
output: [512, 512]
});

let a = [[10,...,5120],...,[10,...,5120]];//developer defined matrix;
let b = [[0.1,...,5.12],...,[0.1,...,5.12]];//developer defined matrix;
let output = multiplyMatrix(a, b);
The first argument for 'gpu.createKernel' method is the function that contains the code to be run on GPU and the second argument is the configuration options object. The following options are provided in the API:
  • output: array or object that describes the output of kernel.
    • as array: [width][width, height], or [width, height, depth]
    • as object: { x: width, y: height, z: depth }
  • outputToTexture: boolean
  • graphical: boolean
  • loopMaxIterations: number
  • constants: object
  • wraparound: boolean
  • hardcodeConstants: boolean
  • floatTextures: boolean
  • floatOutput: boolean
  • functions: array or boolean
  • nativeFunctions: object
  • subKernels: array
In the sample code above 'multiplyMatrix(a, b)' executes the code inside the kernel where a, b are the two matrices that developers can pass as arguments. The thread identifiers 'this.thread.x' and 'this.thread.y' correspond to item positions in the matrix. 

Multiple kernels can be combined into single kernel to get a single output.
const add = gpu.createKernel(function(a, b) {
return a[this.thread.x] + b[this.thread.x];
}).setOutput([20]);

const multiply = gpu.createKernel(function(a, b) {
return a[this.thread.x] * b[this.thread.x];
}).setOutput([20]);

const combinedKernel = gpu.combineKernels(add, multiply, function(a, b, c) {
return multiply(add(a, b), c);
});

combinedKernel(a, b, c);
The 'gpu.combinedKernels' method is used to combine many different kernels. The last argument is a function containing combined logic. The first few arguments contain other dependencies for the combination. 

Similarly multiple kernels can be run at once using the 'gpu.createKernelMap' method and it will return outputs for each individual kernel.

const kernelMap = gpu.createKernelMap({
addResult: function add(a, b) {
return a[this.thread.x] + b[this.thread.x];
},
multiplyResult: function multiply(a, b) {
return a[this.thread.x] * b[this.thread.x];
},
}, function(a, b, c) {
return multiply(add(a, b), c);
});

kernelMap(a, b, c);
// Result: { addResult: [], multiplyResult: [], result: [] }
Here 'addResult' and 'multiplyResult' kernels are individual kernels that are put into a kernel map. The final argument of 'gpu.createKernelMap' function can utilize individual kernels to get a result. The final result is in form of an object where 'addResult' corresponds to 'addResult' kernel, 'multiplyResult' corresponds to 'multiplyResult' kernel and 'result' corresponds to combined kernel. 

Applications:
This library is very useful for tasks where large matrix manipulation is needed like machine learning and image processing. In addition multiple math operations can be done at once using 'createKernelMap' and 'combinKernels' methods without affecting performance. Overall this is a good library to increase code performance by running it on available GPU. 

More details about the API can be found at https://github.com/gpujs/gpu.js .