std::uniform_real_distribution和std::uniform_int_distribution的性能对比

4
我是一名有用的助手,可以进行文本翻译。以下是需要翻译的内容:

我在我的一个应用程序中遇到了性能下降的问题,我将其追溯到随机数据的生成。我编写了一个简单的基准测试,本质上做了同样的事情:

#include <chrono>
#include <iostream>
#include <random>

std::mt19937 random_engine{std::random_device()()};

// Generate one million random numbers
template <typename T, typename Distribution>
std::vector<T> generate_random(Distribution distribution) {
  std::vector<T> data(1000000);

  std::generate_n(data.begin(), 1000000, [&]() {
    return static_cast<T>(distribution(random_engine));
  });
  return data;
}

template <typename T>
std::vector<T> create_data() {
  if constexpr (std::is_same_v<T, float>)
    return generate_random<float>(
        std::uniform_real_distribution<float>(-127.0f, 127.0f));
  if constexpr (std::is_same_v<T, int8_t>)
    return generate_random<int8_t>(
        std::uniform_int_distribution<int32_t>(-127, 127));
}

int main() {
  auto start = std::chrono::system_clock::now();
  auto float_data = create_data<float>();
  std::cout << "Time (float): " << (std::chrono::system_clock::now() - start).count()
            << '\n';

  start = std::chrono::system_clock::now();
  auto int8_data = create_data<int8_t>();
  std::cout << "Time (int8): " << (std::chrono::system_clock::now() - start).count()
            << '\n';

  return 0;
}

在我的电脑上,这个输出结果是:
〉g++ -v
...
Apple clang version 11.0.3 (clang-1103.0.32.29)
Target: x86_64-apple-darwin19.5.0
...

〉g++ tmp.cpp -std=c++17 -O3 && ./a.out
Time (float): 68033
Time (int8): 172771

为什么从真实分布中采样所需的时间比从整数分布中采样所需的时间更少?
更新 libc++和libstdc++表现出完全相反的行为,我仍在研究实现差异所在。参见 libc++ vs. libstdc++

1
测量未经优化的代码是毫无意义的。打开优化(例如-O3),然后看看你得到什么。 - NathanOliver
1
你的代码存在未定义行为。在 std::uniform_int_distribution<int32_t>(127, -127) 中,-127 应该放在前面。即使修复了这个问题,你仍然得到相同的结果,我不确定原因,所以我会留给其他人来解决。 - NathanOliver
可能是由于所选择的特定限制导致了鸽笼拒绝问题。尝试使用2的幂范围,例如(0,128)。 - Mark Ransom
@Bob__ 看起来可能是一个libc++问题:https://quick-bench.com/q/inw74BXJmo7G9c6WkBdFmWvdro8 - NathanOliver
1
结果似乎取决于许多因素。我在编译器探索器上尝试了不同的变体和不同的编译器。使用Clang,它始终为int8_t给出较小的数字,而GCC则相反。使用不同的实现(例如使用特化而不是if constexpr)没有任何区别,除非添加第三个使用int32_t的测试,其中int8_t情况突然改善。最后,在generate_random中删除那个static_cast有助于整体性能。 - Some programmer dude
显示剩余8条评论
1个回答

1

请注意,C++标准并未指定特定的随机数分布算法,包括uniform_int_distributionuniform_real_distribution

因此,您需要调查您所使用的C++标准库的具体实现(对于Clang编译器来说通常很容易,因为它倾向于使用开源库libstdc++)。但是,在生成浮点数(如float)在区间[a,b)和生成相同区间的整数之间存在差异:

浮点数:在大多数实际情况下,给定区间内的浮点数比该区间内的整数更多。通过生成 [0, 1) 范围内的“均匀”伪随机浮点数(例如使用 generate_canonical,但其规范目前存在缺陷),然后将该数字缩放以适应 uniform_real_distribution 给出的范围,实现可以在给定范围内生成“均匀”的伪随机浮点数。这可能涉及使用浮点乘法、除法或其他操作。
整数:在一个范围内生成整数通常涉及生成足够的随机比特以适应该范围,然后使用模数减少或拒绝抽样(后者是无偏的)。该过程往往不会使用浮点运算(与整数运算相比,浮点运算相对较慢),这可能可以解释您发现的性能差异。

谢谢提供链接。这很可能是由于发行版的libc++实现造成的。您的第二点表明,整数分布应该更快地生成,但在这里并非如此。事实上,libstdc++和libc++显示完全相反的行为,因此可能不是由于在生成过程中使用或不使用浮点运算。我还测试了double的性能,它比float慢,但仍然比任何整数类型都要快。 - 137

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接