简体   繁体   中英

std::uniform_real_distribution vs. std::uniform_int_distribution performance

I ran into a performance degradation in one of my applications that I pinpointed to the generation of random data. I wrote a simple benchmark that essentially does the same:

#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;
}

On my machine this outputs:

〉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

Why does sampling from the real distribution take less time than from the int distribution?

UPDATE

libc++ and libstdc++ show completely opposite behaviour. I'm still looking into where the difference in implementation lies. See libc++ vs. libstdc++

Recall that the C++ standard does not specify a particular algorithm for random number distributions, including uniform_int_distribution and uniform_real_distribution .

You will thus have to investigate your particular implementation of the C++ standard library (which will generally be easy for the Clang compiler, since it tends to use the open-source library libstdc++ ). However, there are differences between generating a floating-point number (such as float ) in the interval [a, b) and generating an integer in the same interval:

  • Floating-point numbers: In most practical cases, there are more floating-point numbers in a given interval than there are integers in that interval. An implementation can generate uniform floating-point numbers in a range by producing a uniform random floating-point number in [0, 1) (such as by using generate_canonical , whose specification is unfortunately flawed as of now), then scaling that number to fit the range given by uniform_real_distribution . This can involve the use of floating-point multiplication, division, or other operations.
  • Integers: Generating integers in a range usually involves generating enough random bits to fit the range, then using modulo reduction or rejection sampling (and the latter is unbiased). The process will tend not to use floating-point operations (which are relatively slow compared to integer operations), which could explain the performance difference you have discovered.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM