JavaScriptでGPUを簡単に扱えるライブラリ「GPU.js」レビュー、並列処理で多次元の演算が爆速に
![](https://i.gzn.jp/img/2020/08/05/gpu-js/00_m.png)
GPUを用いた並列演算に利用されるプログラミング言語といえば、機械学習の研究などに利用されるPythonが一般的ですが、JavaScriptによるウェブアプリでもGPUを用いたい場合があるはず。オープンソースのJavaScriptライブラリ「GPU.js」を使うと、スクリプトを実行するコンピューターのGPUを利用して並列処理を行うことで、多次元の演算などを高速に行うことができます。
GPU.js - GPU accelerated JavaScript
https://gpu.rocks
今回GPU.jsを動かすシステムは以下。CPUはIntel Core i5-4570、GPUはAMD Radeon RX480、OSはUbuntu 20.04、ウィンドウシステムはWaylandを使用しています。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/010_m.png)
カーネルバージョンは5.4.0-42
![](https://i.gzn.jp/img/2020/08/05/gpu-js/030_m.png)
ロードしているドライバーはこんな感じ。Linuxに標準で組み込まれているドライバーを使用しています。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/020_m.png)
Node.jsでGPU.jsを利用したJavaScriptを実行するため、下記コマンドでパッケージをインストールしておきます。なお、今回利用しているNode.jsのバージョンは12.8.2です。
npm install gpu.js --save
Node.jsでのGPU.jsの使い方はこんな感じ。まずはrequireでgpu.jsを読み込みます。
const { GPU } = require('gpu.js');
続いて「GPU」オブジェクトを初期化して「gpu」インスタンスを作成。
const gpu = new GPU();
作成したgpuインスタンスのメソッド「createKernel」において、GPUで実行したい計算を記述していきます。
const multiplyMatrix = gpu.createKernel(function(a, b) { let sum = 0; for (let i = 0; i < 1024; i++) { sum += a[this.thread.y][i] * b[i][this.thread.x]; }
まずはGPUとの比較対象として、2次元の行列演算を含むJavaScriptをCPUだけで実行してみます。インスタンスを作成する際に「{ mode: 'cpu', }」と指定しておくと、CPUを使用した計算を行うことができます。
const generateMatrices = () => { const matrices = [[], []] for (let y = 0; y < 1024; y++){ matrices[0].push([]) matrices[1].push([]) for (let x = 0; x < 1024; x++){ matrices[0][y].push(Math.random()) matrices[1][y].push(Math.random()) } } return matrices } const { GPU } = require('gpu.js'); const gpu = new GPU({ mode: 'cpu', }); const multiplyMatrix = gpu.createKernel(function(a, b) { let sum = 0; for (let i = 0; i < 1024; i++) { sum += a[this.thread.y][i] * b[i][this.thread.x]; } return sum; }).setOutput([1024, 1024]) const matrices = generateMatrices() const out = multiplyMatrix(matrices[0], matrices[1]) console.log(out[10][12])
実行にかかった時間をtimeコマンドで調べた結果はこんな感じ。実際のプログラム実行にかかった時間は「external」に表示されており、実行に21.58秒かかったことを表してます。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/040_m.png)
次はGPUを使用して計算してみます。「GPU」インスタンスを作成する際のオプションを削除し、もう一度スクリプトを実行してみます。
const gpu = new GPU();
実行結果は以下。実行時間は555.28ミリ秒(約0.56秒)と、CPUで実行した場合よりもはるかに高速であることがわかります。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/050_m.png)
次に、通常の画像を表示する際の速度を検証してみます。まずは以下のHTMLを使用して、GPU.jsを使わない場合の表示速度を測定。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8" /> <title>GPU.js テスト</title> </head> <body> <script> document.write('<img src="image/source1.jpg"') </script> </body> </html>
実行結果はこんな感じ。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/060_m.jpg)
GPU.jsによる処理を用いて画像を表示した場合の表示速度を調べてみます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8" /> <title>GPU.js テスト</title> <script src="dist/gpu-browser.js"></script> </head> <body> <script> const gpu = new GPU(); const kernel = gpu.createKernel(function(image) { const pixel = image[this.thread.y][this.thread.x]; this.color(pixel[0], pixel[1], pixel[2], pixel[3]); }) .setGraphical(true) .setOutput([4032, 3024]); const image = document.createElement('img'); image.src = 'image/source1.jpg'; image.onload = () => { kernel(image); // Result: colorful image document.getElementsByTagName('body')[0].appendChild(kernel.canvas); }; </script> </body> </html>
GPU.jsを使用しない場合に比べて「idle」以外にかかる秒数が増えてしまいました。当然ではありますが、単純に画像を表示するといった並列処理が不要な用途には適していないようです。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/070_m.jpg)
また、GPU.jsには実装の参考になる豊富なサンプルも用意されています。例えばゆっくりと色が変化する「slow-fade.html」を表示させるとこんな感じ。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/080_m.png)
GPUモードでは、Chrome DevToolsで演算時間にあたる「scripting」が250ミリ秒ほどですが……
![](https://i.gzn.jp/img/2020/08/05/gpu-js/090_m.png)
CPUモードに変更すると、1700ミリ秒もかかってしまっています。GPUによる並列演算の威力がよくわかります。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/100_m.png)
他にもレイトレーシングを実装している「parallel-raytracer.html」などがサンプルとして用意されています。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/110_m.png)
GPU.jsを使った場合の効果を簡単に測定できるベンチマークサイトも用意されています。
GPU.js - GPU accelerated JavaScript
https://gpu.rocks/#/benchmark
ブラウザにURLでアクセスして「BENCHMARK!」をクリック。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/200_m.png)
ベンチマーク結果が表示されました。上がGPUを利用した場合のスコア、下がCPUを利用した場合のスコアです。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/210_m.png)
Intel Core i7-6500Uを搭載した外付けGPUのないノートPCの計測結果は以下。内蔵GPUでも処理は速くなることがわかります。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/220_m.png)
Pixel3もGPUとしてAdreno 630を搭載しているのでベンチマークを計測してみると、やはりCPUのみの場合よりもGPUを利用した場合のほうが処理速度が向上しています。GPUに対応していないデバイスからのアクセスは自動でCPUによる計算にフォールバックされるので、GPU.jsを組み込んだページが一部デバイスで表示されないという事態は回避可能。多次元ベクトルなどの演算をJavaScriptで行う場合は、GPU.jsを組み込むとパフォーマンス向上が期待できそうです。
![](https://i.gzn.jp/img/2020/08/05/gpu-js/230_m.png)
GPU.jsのソースコードはGitHubに公開されており、誰でも利用することができます。
GitHub - gpujs/gpu.js: GPU Accelerated JavaScript
https://github.com/gpujs/gpu.js/#gpucreatekernel-settings
・関連記事
どのウェブフレームワークが一番高速に動作するのかが一目で分かる「Web Framework Benchmarks」レビュー - GIGAZINE
お手軽にウェブアプリを開発できるOSSのフレームワーク「Phoenix Framework」、15分でTwitterライクなアプリを開発するムービーも - GIGAZINE
ウェブアプリ開発で使えるUIフレームワークのコンポーネントをまとめて確認できる「Open UI」 - GIGAZINE
Pythonでコードを書いてAWSやKubernetesのシステム構成図を出力できる「Diagrams」 - GIGAZINE
無料のRPGをプレイするだけで未経験者でもプログラミングを習得可能な「コードクロニクル」でPythonをマスターしてみた - GIGAZINE
・関連コンテンツ
in レビュー, ソフトウェア, Posted by darkhorse_log
You can read the machine translated English article A library ``GPU.js'' that can ea….