为何WebAssembly中sin函数性能远低于JavaScript?
关于WebAssembly与JavaScript三角函数性能差异的基准测试分析
我用Catch2运行了一组简单的基准测试,通过emscripten 3.1.37以-O3优化级别编译,测试代码如下:
BENCHMARK("cpp sin only") { double sum = 1.0; for (int t = 0; t < 2000000; ++t) { sum += sin(double(t)); } return sum; }; #ifdef __EMSCRIPTEN__ BENCHMARK("js sin only") { EM_ASM_DOUBLE({ let sum = 1; for (let i = 0; i < 2000000; i++) { sum = sum + Math.sin(i); } return sum; }); }; #endif
原本我认为JavaScript与WebAssembly的性能不会有显著差异,但实际结果相差悬殊:
chrome: benchmark name samples iterations est run time mean low mean high mean std dev low std dev high std dev ------------------------------------------------------------------------------- cpp sin only 100 1 7.93775 s 79.3856 ms 79.147 ms 79.7195 ms 1.43061 ms 1.10437 ms 1.97222 ms js sin only 100 1 2.21506 s 22.1354 ms 22.0064 ms 22.3 ms 742.138 us 614.746 us 901.128 us
使用GCC 12.3.0原生编译时,耗时为24.2ms。
- 我了解JavaScript所有数字均采用双精度浮点数,因此对比是公平的。若在C++版本中使用float类型,Chrome中耗时降至12ms,但仍慢于JavaScript(且精度更低),Firefox中则约为30ms。
或许JavaScript使用了精度较低但速度更快的sin和sqrt实现?添加-fast-math并未提升双精度版本的性能。启用-fast-math的float版本在Chrome中与JavaScript速度相当,但在Firefox中仍约为30ms。- 是否是WebAssembly获得的优化器时间不足?这或许能解释其在Firefox中性能更差的原因,但emscripten难道不应负责大部分优化工作吗?
- 会不会是Meltdown/Spectre防护机制导致的?
更新(应评论要求,补充更多基准测试结果)
- g++12比clang15略快,但差距在10%以内
- 各版本的sqrt性能几乎持平(此前示例中同时包含sqrt和sin)
- 大部分时间消耗在sin函数上
- 将迭代次数增加到200万时,JavaScript速度约为WebAssembly的4倍;增加到500万时,领先优势扩大至10倍。JavaScript仍与原生C++速度相当。
- 注:该基准测试由Catch2执行100次,上述代码的总运行时间约为1秒。
- 已验证并非JavaScript使用float类型导致差异,WebAssembly C++计算结果与JavaScript完全一致。
- 使用123456789042+1000000作为输入时,gcc、clang原生版本、WebAssembly C++版本和JavaScript的运行时间均增加了约3-4倍(WebAssembly与JavaScript的相对性能保持不变)。
- 参考代码和Chrome中的测试结果已留存。
更新2
在评论建议下,已向emscripten项目提交该问题。
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

