Web快適度測定について

今回は、本サイト上でブラウザ上でベンチマークを行うことができる「Web快適度測定」というツールについて、なぜこれを作ったのか、そして中身はどうなっているのか、少しだけ深掘りして紹介してみようと思います。
開発の経緯:対応デバイスを決めるの、むずくない?
この機能を作ったきっかけは、現在業務で開発しているサービスの「対応デバイス」を検討していた時のことでした。
「AndroidはOS12以上」「Safari 16以上」「Chrome 110以上」といった基準はよくありますが、実際にはスペックの低いAndroid端末もあれば、OSやブラウザは最新でもパワーが不足しているデバイス、逆に古くてもパワーのある旧ハイエンド端末もあったりします。 開発チームで「どこまでの低スペック端末を切り捨てるか」を議論する際、明確な基準がないことに非常に苦労しました。
そこで、「ブラウザ上で手軽に動かせて、その端末の『Web的な実力』を数値化できるベンチマークがあれば、そのスコアを基準に対応デバイスを定義できるのでは?」と思いついたのが、この機能の始まりです。
Web快適度測定の基本仕様
このツールは、大きく分けて2つの測定を行います。
- 演算性能の測定(スコア化)
端末のCPUパワーを測定し、シングルコア・マルチコアそれぞれのスコアとランク(S〜F)を出します。 - ブラウザ機能サポート状況
最新のWeb API(WebGPU, Passkeys, WebP等)がそのブラウザで使えるかをチェックします。
スコアとサポー ト状況は計測後、画面に表示されます。
また、測定して終わりではなく、結果を誰かに伝えたり記録したりしやすいよう、共有機能も追加しています。ブラウザの Web Share API を使ってOS標準の共有メニューを直接呼び出せます。HTML5 Canvas を使用し、スコア・ランク・環境情報を表示した結果画像を自動で作成し、そのままX(旧Twitter)やLINE、メッセージアプリへ送信できます。
ベンチマークの処理の詳細
1. Web Workerによる正確な測定
ベンチマーク中、メインスレッド(UIを動かすスレッド)が固まらないようにするため、演算処理はすべてWeb Workerで実行するようにしています。
// Web Worker 上で実行される演算ロジックの一部
const workerScript = `
self.onmessage = function(e) {
// 1. 浮動小数点演算 (sin, cos, sqrt)
let floatSum = 0;
for (let i = 0; i < 10000000; i++) {
floatSum += Math.sin(i) * Math.cos(i) + Math.sqrt(i);
}
// 2. 整数・ビット演算
let intSum = 0;
for (let i = 0; i < 50000000; i++) {
intSum += (i * 1337) ^ (i << 2);
}
// 3. メモリアクセス
const size = 5000000;
const arr = new Int32Array(size);
// ... 配列へのランダムアクセス処理
// 処理終了をメインスレッドに通知
self.postMessage({ type: 'done', avgTime: 123 /* 実際は計算値 */ });
};
`;
// Web Workerを生成して実行し、結果をPromiseで返す関数
function runWorkerTask(passes) {
return new Promise((resolve) => {
// 文字列として定義したスクリプトをBlob化してWorkerとして読み込む
const blob = new Blob([workerScript], { type: "application/javascript" });
const worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = (e) => {
if (e.data.type === "done") {
worker.terminate(); // 終わったらWorkerを破棄
resolve(e.data.avgTime);
}
};
// Workerに実行命令(パス回数など)を送信
worker.postMessage({ passes });
});
}
2. マルチコア測定の実装
デバイスの論理コア数(navigator.hardwareConcurrency)を取得し、その数だけ同時に Worker を立ち上げて並列処理を行わせます。Promise.all を活用して全ての Worker の完了を待機し、総合的なスループットからスコアを算出するようにしました。これにより、最近の多コアスマホの実力をしっかり引き出せるようにしています。
async function runMultiCoreBenchmark(cores, passes, onProgress) {
const workers = [];
const startAll = performance.now();
let completedPasses = 0;
const totalPasses = passes * cores;
// デバイスの論理コア数分だけ Worker を作成して実行
for (let i = 0; i < cores; i++) {
workers.push(
runWorkerTask(passes, () => {
completedPasses++;
// 全体の進捗率を計算してUIに通知
if (onProgress) {
const percent = Math.floor((completedPasses / totalPasses) * 100);
onProgress(`マルチコア測定中... (${percent}%)`);
}
}),
);
}
// すべての並列処理が終わるまで待機
await Promise.all(workers);
const endAll = performance.now();
// 実行時間から最終的なマルチコアスコアを計算
const totalTime = endAll - startAll;
return Math.floor((500_000 * cores * passes) / totalTime);
}