SWCとBabelのパフォーマンス比較
JavaScriptはシングルスレッドです。JSスレッドは重い計算を行うのに適した場所ではありません。計算負荷の高いbabel
とswc
について話しましょう。
同期ベンチマーク
シングルコアのワークロードのベンチマークを行いましょう。これはtransformSync
を使用していることに注意してください。これは実際にはめったに使用されません。
[transform]
swc (es3) x 616 ops/sec ±4.36% (88 runs sampled)
swc (es2015) x 677 ops/sec ±2.01% (90 runs sampled)
swc (es2016) x 1,963 ops/sec ±0.45% (93 runs sampled)
swc (es2017) x 1,971 ops/sec ±0.35% (94 runs sampled)
swc (es2018) x 2,555 ops/sec ±0.35% (93 runs sampled)
swc-optimize (es3) x 645 ops/sec ±0.40% (90 runs sampled)
babel (es5) x 34.05 ops/sec ±1.15% (58 runs sampled)
SWCは非常に高速です。swc (es3)
はbabel (es5)
よりも多くの処理を行いますが、swc (es3)
はbabel (es5)
よりも高速です。
実世界ベンチマーク
transformSync
とtransformFileSync
は現在のスレッドをブロックするため、実世界ではめったに使用されません。await Promise.all()
は、より優れているため頻繁に使用されます。
for (const promise of promises) {
await promise;
}
Promise.all()
を使用した実際の使用状況のベンチマークを作成しましょう。
理想的なケース
まず、理想的なケースのベンチマークを作成しました。これは、物理CPUコアの数であるn
個のプロミスを一度に呼び出します。詳細なコードについては、node-swcリポジトリ (新しいタブで開く)を参照してください。
const os = require("os");
const cpuCount = os.cpus().length;
const SOURCE = `
// See the link above
`;
const SUITES = [
// ...
// See the link above
];
const arr = [];
for (let i = 0; i < cpuCount / 2; i++) {
arr.push(0);
}
console.info(`CPU Core: ${cpuCount}; Parallelism: ${arr.length}`);
console.info(
`Note that output of this benchmark should be multiplied by ${arr.length} as this test uses Promise.all`
);
SUITES.map(args => {
const [name, requirePath, fn] = args;
const func = fn.bind(null, require(requirePath));
bench(name, async done => {
await Promise.all(arr.map(v => func()));
done();
});
});
古いデスクトップでベンチマークを実行しました。E3-v1275と24GBのRAMを搭載しています。以下の出力は、ベンチマーク出力からそのままコピーしたものです。
CPU Core: 8; Parallelism: 4
Note that output of this benchmark should be multiplied by 4 as this test uses Promise.all
[multicore]
swc (es3) x 426 ops/sec ±3.75% (73 runs sampled)
swc (es2015) x 422 ops/sec ±3.57% (74 runs sampled)
swc (es2016) x 987 ops/sec ±2.53% (75 runs sampled)
swc (es2017) x 987 ops/sec ±3.44% (75 runs sampled)
swc (es2018) x 1,221 ops/sec ±2.46% (77 runs sampled)
swc-optimize (es3) x 429 ops/sec ±1.94% (82 runs sampled)
babel (es5) x 6.82 ops/sec ±17.18% (40 runs sampled)
ここで、反復ごとに4つの操作を実行するため、これを4倍する必要があります。
swc (es3) x 1704 ops/sec ±3.75% (73 runs sampled)
swc (es2015) x 1688 ops/sec ±3.57% (74 runs sampled)
swc (es2016) x 3948 ops/sec ±2.53% (75 runs sampled)
swc (es2017) x 3948 ops/sec ±3.44% (75 runs sampled)
swc (es2018) x 4884 ops/sec ±2.46% (77 runs sampled)
swc-optimize (es3) x 1716 ops/sec ±1.94% (82 runs sampled)
babel (es5) x 27.28 ops/sec ±17.18% (40 runs sampled)
これが実際の結果です。
babel (es5)
のパフォーマンスは低下しています。非同期は無料ではありません。それでも、34.05 ops/秒
=> 27.28 ops/秒
は予想よりもはるかに優れています。
多数の操作のベンチマーク
反復ごとに100個のプロミスを作成するようにベンチマークファイルをわずかに変更しました。
CPU Core: 8; Parallelism: 100
Note that output of this benchmark should be multiplied by 100 as this test uses Promise.all
[multicore]
swc (es3) x 21.99 ops/sec ±1.83% (54 runs sampled)
swc (es2015) x 19.11 ops/sec ±3.39% (48 runs sampled)
swc (es2016) x 55.80 ops/sec ±6.97% (71 runs sampled)
swc (es2017) x 62.59 ops/sec ±2.12% (74 runs sampled)
swc (es2018) x 81.08 ops/sec ±5.22% (75 runs sampled)
swc-optimize (es3) x 18.60 ops/sec ±2.13% (50 runs sampled)
babel (es5) x 0.32 ops/sec ±19.10% (6 runs sampled)
上記のように100倍する必要があります。
swc (es3) x 2199 ops/sec ±1.83% (54 runs sampled)
swc (es2015) x 1911 ops/sec ±3.39% (48 runs sampled)
swc (es2016) x 5580 ops/sec ±6.97% (71 runs sampled)
swc (es2017) x 6259 ops/sec ±2.12% (74 runs sampled)
swc (es2018) x 8108 ops/sec ±5.22% (75 runs sampled)
swc-optimize (es3) x 1860 ops/sec ±2.13% (50 runs sampled)
babel (es5) x 32 ops/sec ±19.10% (6 runs sampled)
SWCのパフォーマンスが大幅に低下しないのはなぜでしょうか?その秘密はNode.jsにあります。Node.jsは内部的にワーカースレッドプールを管理し、SWCはそれ上で実行されます。したがって、100個のプロミスを一度に作成しても、ワーカースレッドの数はそれよりもはるかに少なくなります。
結論
名前 | 1コア、同期 | 4つのプロミス | 100個のプロミス |
---|---|---|---|
swc (es3) | 616 ops/秒 | 1704 ops/秒 | 2199 ops/秒 |
swc (es2015) | 677 ops/秒 | 1688 ops/秒 | 1911 ops/秒 |
swc (es2016) | 1963 ops/秒 | 3948 ops/秒 | 5580 ops/秒 |
swc (es2017) | 1971 ops/秒 | 3948 ops/秒 | 6259 ops/秒 |
swc (es2018) | 2555 ops/秒 | 4884 ops/秒 | 8108 ops/秒 |
swc-optimize (es3) | 645 ops/秒 | 1716 ops/秒 | 1860 ops/秒 |
babel (es5) | 34.05 ops/秒 | 27.28 ops/秒 | 32 ops/秒 |
swc
は、ほとんどすべての作業をワーカースレッドで行うため、うまくスケールします。100個のプロミス
のスループットが4つのプロミス
よりも優れていたという事実から、Node.jsのワーカースレッドプールがハイパースレッディングを活用していると結論付けることができます。
swc
はCPUコアの数に応じてスケールアップします。Promise.all
はスケーリングに十分です。