【问题标题】:clang performance drop when using uniform_real_distribution使用 uniform_real_distribution 时 clang 性能下降
【发布时间】:2020-02-05 13:37:30
【问题描述】:

以下代码在使用uniform_real_distribution 时导致g++ 和clang++ 的时间非常不同。

#include <iostream>
#include <sstream>
#include <fstream>

#include <chrono>
#include <random>


std::mt19937::result_type seed = 0;
std::mt19937 gen(seed);
// std::uniform_int_distribution<size_t> distr(0, 1);
std::uniform_real_distribution<double> distr(0.0,1.0);

int main()
{
    auto t_start = std::chrono::steady_clock::now();
    for (auto i = 1; i <= 1000000; ++i)
    {
        distr(gen);
    }
    auto t_end = std::chrono::steady_clock::now();
    std::cout << "elapsed time: " << std::chrono::duration_cast<std::chrono::nanoseconds>(t_end - t_start).count()  << " ns\n" << std::endl;

    return 0;
}

使用以下命令编译:

clang++ -std=c++17 -O3 -flto -march=native -mllvm -inline-threshold=10000000 rng.cpp -o rng
g++ -std=c++17 -O3 -march=native rng.cpp -o rng

这会导致以下时间:

clang:  272929774 ns

gcc:    12054635 ns

当使用注释分布时,时间是:

clang:  48155862 ns

gcc:    50226810 ns

我在这里发现了一个很老的问题,它处理了同样的问题,但是在我的情况下,所提出的解决方案都不起作用。

Clang performance drop for specific C++ random number generation

有人知道这里发生了什么吗?

【问题讨论】:

  • -O3 很危险,尤其是在使用浮点数时!如果您正在使用编译标志watch this.
  • 试试distr(1.0, 2.0);
  • -O2 指向同一张图片。不幸的是,distr(1.0, 2.0); 也无济于事。
  • 在执行这些微基准测试时,始终验证您的代码没有被优化掉!
  • 是的,如下所述,如果可能的话,如果代码能被优化掉,我真的很高兴,所以这里的问题应该是,为什么 clang 不能做到这一点?

标签: c++ performance gcc distribution clang++


【解决方案1】:

看看godbolt

在 gcc 编译器上废弃了distr(gen);!!!

.L27:
        dec     esi
        je      .L25

这是什么都不做的for循环!

关于 clang 编译器不够聪明:

.LBB0_1:                                # =>This Inner Loop Header: Depth=1
        mov     edi, offset gen
        call    double std::generate_canonical<double, 53ul, std::mersenne_twister_engine<unsigned long, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul, 4294967295ul, 7ul, 2636928640ul, 15ul, 4022730752ul, 18ul, 1812433253ul> >(std::mersenne_twister_engine<unsigned long, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul, 4294967295ul, 7ul, 2636928640ul, 15ul, 4022730752ul, 18ul, 1812433253ul>&)
        dec     ebx
        jne     .LBB0_1

generate_canonical 确实被调用了。

基本上你必须使用distr(gen); 的结果来做一些对代码结果有影响的事情,否则编译器可以删除该代码。


simplest way to fix it 是将distr(gen); 的结果累加并打印出来。

现在当您查看汇编时,您可以看到 clang 正在调用函数 std::generate_canonical&lt;double, 53ul, std::mersenne_twister_engine&lt; .... &gt;&gt;gcc 只是将相应的代码内联。

这种差异很可能是由标准库的不同组织引起的。 Clang 使用的版本内置于标准库和来自头文件的 gcc 模板中,用于在刚刚创建的程序集中生成代码。当编译器从库中获取外部代码时,它无法判断它到底做了什么,因此无法优化掉该代码(因为一些副作用可以隐藏在库中)。

【讨论】:

  • 您好,您能想出一个好方法吗?但是,在首先出现问题的“真实”代码中,我肯定会使用这个值,所以这可能不是这里唯一的原因......
  • 顺便说一句,如果我能找到 clang 无法优化它的原因,那就太好了。我对这个优化很感兴趣。
  • >> 这种差异很可能是由标准库的不同组织引起的。是的,这就是重点。可能它甚至是一个错误,因为此时它确实降低了性能。在执行模拟时,您基本上一直在使用随机数,所以我的实际模拟在使用 clang 时变得慢了一倍......感谢您的建议和解释。
  • 修复后您是否进行了测量?我怀疑当distr(gen); 没有被 gcc 丢弃时差异如此之大。
  • 是的,我已经用你建议的版本进行了测量,结果是:clang: acc: 1.50021e+06 elapsed time: 328040263 ns gcc: acc: 1.50021e+06 elapsed time: 64385650 ns
猜你喜欢
  • 2011-11-07
  • 1970-01-01
  • 2014-03-02
  • 2014-06-08
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多