为什么 sin 在 WebAssembly 中比在 Java 脚本中慢?

Ada*_*dam 6 webassembly

我有一些非常简单的基准测试,通过 Catch2 运行,并使用 -O3 进行编译emscripten 3.1.37

    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
Run Code Online (Sandbox Code Playgroud)

我预计 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 
Run Code Online (Sandbox Code Playgroud)

本机,用 编译GCC 12.3.0,我得到 24.2ms。

  • 据我了解,JavaScript 对所有数字都使用双精度浮点数。所以比较应该是公平的。在 C++ 版本中使用 float 时,在 chrome 中会达到 12 毫秒,但这仍然较慢(且精度较低)。FF 约为 30 毫秒。
  • 也许 JavaScript 使用不太精确但更快的 sin 和 sqrt 实现?添加-fast-math不会提高 double 的性能。Chrome 中的快速数学浮动变得与 JavaScript 一样快,在 FF 中仍然在 30 毫秒左右。
  • 是不是 WebAssembly 没有在优化器中获得足够的时间?这可以解释为什么 FF 的速度慢得多。但是 emscripten 不应该负责大部分优化吗?
  • 这可能是某种防止崩溃/幽灵的保护措施吗?

更新(我根据评论中的请求运行了几个额外的基准测试):

  • g++12 比 clang15 快一点,但在 10% 以内
  • sqrt 的性能在各个版本之间几乎相同(在示例中存在 sqrt 和 sin 之前)
  • 大部分时间都在罪恶中度过。
  • 将迭代次数增加到 200 万次使 JS 速度提高约 4 倍。将其增加到 500 万,领先优势增加到 10 倍。JS 的速度仍然与原生 C++ 大致相同。
  • 请注意,Catch2 执行了 100 次基准测试。上述代码的运行时间约为1s。
  • 我验证了一下,并不像JS使用float那么简单。WebAssembly C++ 计算与 JS 结果完全匹配。
  • 使用 123456789042+1000000 将 gcc、clang native、webAssembly C++ 和 js 的运行时间提高了约 3-4 倍(WebAssembly 与 js 的相对性能保持不变)。
  • 参考,这是我使用的代码: https: //pastebin.com/Mu2barB6,这是 chrome 的结果: https: //pastebin.com/Hbte7yRj