
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、このブログでは言語的にはPHP
と並んでJavaScript
に関する記事も多く公開してきました。
というのも、JavaScript
は特にここ5年ほどで急速に発展し、人気を獲得しているからです。
ただ、JavaScript
での処理が多くなってくると、たまに問題点が発生することもあります。
それは・・・
処理が遅くなる
という点です。
どうしてもブラウザでの処理は、ネイティブアプリと比べるとスピードで劣ってしまうのはよく知られていることです。
ただ、こんな状況を大きく変えるかもしれない技術があるのをご存知でしょうか。
それが今回テーマの・・・
WebAssembly(うぇぶあせんぶりー)
です。
そこで!
今回はWebAssembly
を使ってJavaScript
の処理を高速化する基本的な使い方をご紹介します。また、最後に「どれだけ処理が早くなっているか」を検証するために、WebAssembly
を使ったコードとJavaScript
だけのコードでスピードを比較してみたいと思います。
ぜひ皆さんのお役に立てると嬉しいです
開発環境: Google Chrome 79
目次 [非表示]
WebAssemblyとは?
WebAssembly
とは結局何かという話なんですが、簡単に言うと「処理が重い部分だけ高速なコードをつかっちゃおう!」という技術です。
車で言うと、最初と最後下道だけど、途中は高速道路を使ってビューン!って時間を短縮するイメージでしょうか。
(イメージ)
やりたいこと
今回はテストとして、「1〜300,000の数字の間に素数がいくつあるか」をカウントするコードを実装していきます。(なぜ中途半端な数字かというと、それ以上だと時間がかかりすぎてしまったからです。そのため、数字は皆さんのPC環境に合わせて変更してください)
WebAssemblyのファイル「.wasm」をつくる
WebAssembly
はC言語などのコードをコンパイルして使うことになりますが、最初は環境を構築するだけでゲンナリ・・・となってしまうと思うので、今回はオンライン上でコンパイルすることができるWasmFiddleというサービスを利用してやってみましょう。
このページを開くと、左側にC言語のコードを書いていく部分があるので、以下のコードを貼り付けてください。
#include <stdbool.h>
bool isPrime(num) {
for(int i = 2; i < num; i++) {
if(num % i == 0) return false;
}
return num > 1;
}
int countPrimeNumbers(maxNumber) {
int count = 0;
for(int i = 0; i < maxNumber; i++) {
if(isPrime(i)) {
count++;
}
}
return count;
}
コードを貼り付けたら、ページ上部にある「Build」をクリックします。
ビルドが完了したらページ中程にある「Wasm」をクリックしてください。
すると、自動で「program.wasm」というファイルがダウンロードされます。これがWebAssembly
のコードになりますので、今回は「count_prime_numbers.wasm」という名前に変更してhttp://******/wasm/count_prime_numbers.wasm
でアクセスできるようにサイトに設置します。
※ この例はLaravel
を使った例ですので、ご自身の環境に合わせてファイルを設置してください。
WebAssemblyでコードをつくる
では、実際にブラウザからWebAssembly
を使ってみましょう。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
const maxNumber = 300000;
fetch('/wasm/count_prime_numbers.wasm')
.then(response => {
return response.arrayBuffer();
})
.then(bytes => {
return WebAssembly.instantiate(bytes);
})
.then(obj => {
console.log('1 〜 '+ maxNumber +'にある素数の数は、'+ obj.instance.exports.countPrimeNumbers(maxNumber) +' 個');
});
</script>
</body>
</html>
この中でやっていることは、まず先ほど用意したcount_prime_numbers.wasm
にfetch()
を使ってAjaxでアクセスし、その内容を使ってWebAssembly
を起動、そして(WebAssembly
内で)素数をカウントするという流れになっています。
JavaScript
内での使い方は簡単かと思います。
スピードを比較してみる
では、次にWebAssembly
を使った場合と、JavaScript
だけで実行した場合はどれぐらい処理スピードが違うかを検証してみます。
まずはWebAssembly
を使った場合のコードです。(先ほどのコードに実行時間が分かるコードをつけ足しました)
/*** WebAssembly ***/
const maxNumber = 300000;
const startTime = performance.now();
fetch('/wasm/count_prime_numbers.wasm')
.then(response => {
return response.arrayBuffer();
})
.then(bytes => {
return WebAssembly.instantiate(bytes);
})
.then(obj => {
console.log('1 〜 '+ maxNumber +'にある素数の数は、'+ obj.instance.exports.countPrimeNumbers(maxNumber) +' 個');
const endTime = performance.now();
console.log('計算にかかった時間: '+ (endTime - startTime) +' ミリ秒');
});
では、実行結果です。
約14秒かかってますね。
では、同じ内容をJavaScriptだけで実行してみましょう。
コードはこちらです。
/*** JavaScript ***/
const maxNumber = 300000;
const startTime = performance.now();
const isPrime = num => {
for(let i = 2; i < num; i++) {
if(num % i === 0) {
return false;
}
}
return (num > 1);
};
const countPrimeNumbers = maxNumber => {
let count = 0;
for(let i = 0; i < maxNumber; i++) {
if(isPrime(i)) {
count++;
}
}
return count;
};
console.log('1 〜 '+ maxNumber +'にある素数の数は、'+ countPrimeNumbers(maxNumber) +' 個');
const endTime = performance.now();
console.log('計算にかかった時間: '+ (endTime - startTime) +' ミリ秒');
実行結果はこちら。
なんと、約23秒なので、さっきと比べると1.7倍ほどの時間がかかっています。
やはりWebAssembly
は高速ということになりますね。
いいですね
ちなみに:サポートしているブラウザ
2019.12.24現在、Can I useを見るとIE以外の主要なブラウザはほぼ全てWebAssembly
をサポートしています。
おわりに
ということで、今回は私も個人的に気になっていたWebAssembly
を試してみました。
なぜ注目していたかと言うと、もしVue
やReact
などがフルにWebAssembly
化するとコードが早くなるだろうと考えていたからです。(それって技術的に可能かな??)
とにかく、もし皆さんのサイトにあるJavaScript
のスピードが遅い場合はWebAssembly
を使ってみるのもいいかもしれません。
ぜひ活用してくださいね。
ではでは〜!