repetir - generar numeros aleatorios en un arreglo en c++
Caída del rendimiento Clang para la generación de números aleatorios C++ específicos (1)
Utilizando el módulo aleatorio de C ++ 11, encontré una caída de rendimiento impar cuando uso std::mt19937
(versiones de 32 y 64 bits) en combinación con un uniform_real_distribution
(float o double, no importa). Comparado con una compilación de g ++, ¡es más que un orden de magnitud más lento!
El culpable no es solo el generador mt, ya que es rápido con uniform_int_distribution
. Y no es un defecto general en el uniform_real_distribution
ya que es rápido con otros generadores como default_random_engine
. Solo esa combinación específica es extrañamente lenta.
No estoy muy familiarizado con las características intrínsecas, pero el algoritmo de Mersenne Twister está más o menos estrictamente definido, por lo que una diferencia en la implementación no podría explicar esta diferencia, supongo. Programa de medida está siguiendo, pero aquí están mis resultados para clang 3.4 y gcc 4.8.1 en una máquina de 64 bits en Linux:
gcc 4.8.1
runtime_int_default: 185.6
runtime_int_mt: 179.198
runtime_int_mt_64: 175.195
runtime_float_default: 45.375
runtime_float_mt: 58.144
runtime_float_mt_64: 94.188
clang 3.4
runtime_int_default: 215.096
runtime_int_mt: 201.064
runtime_int_mt_64: 199.836
runtime_float_default: 55.143
runtime_float_mt: 744.072 <--- this and
runtime_float_mt_64: 783.293 <- this is slow
Programa para generar esto y prueba tú mismo:
#include <iostream>
#include <vector>
#include <chrono>
#include <random>
template< typename T_rng, typename T_dist>
double time_rngs(T_rng& rng, T_dist& dist, int n){
std::vector< typename T_dist::result_type > vec(n, 0);
auto t1 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < n; ++i)
vec[i] = dist(rng);
auto t2 = std::chrono::high_resolution_clock::now();
auto runtime = std::chrono::duration_cast<std::chrono::microseconds>(t2-t1).count()/1000.0;
auto sum = vec[0]; //access to avoid compiler skipping
return runtime;
}
int main(){
const int n = 10000000;
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine rng_default(seed);
std::mt19937 rng_mt (seed);
std::mt19937_64 rng_mt_64 (seed);
std::uniform_int_distribution<int> dist_int(0,1000);
std::uniform_real_distribution<float> dist_float(0.0, 1.0);
// print max values
std::cout << "rng_default_random.max(): " << rng_default.max() << std::endl;
std::cout << "rng_mt.max(): " << rng_mt.max() << std::endl;
std::cout << "rng_mt_64.max(): " << rng_mt_64.max() << std::endl << std::endl;
std::cout << "runtime_int_default: " << time_rngs(rng_default, dist_int, n) << std::endl;
std::cout << "runtime_int_mt: " << time_rngs(rng_mt_64, dist_int, n) << std::endl;
std::cout << "runtime_int_mt_64: " << time_rngs(rng_mt_64, dist_int, n) << std::endl;
std::cout << "runtime_float_default: " << time_rngs(rng_default, dist_float, n) << std::endl;
std::cout << "runtime_float_mt: " << time_rngs(rng_mt, dist_float, n) << std::endl;
std::cout << "runtime_float_mt_64: " << time_rngs(rng_mt_64, dist_float, n) << std::endl;
}
compilar a través de clang++ -O3 -std=c++11 random.cpp
o g ++ respectivamente. ¿Algunas ideas?
editar: Finalmente, Matthieu M. tuvo una gran idea: el culpable es inline, o más bien una falta de eso. Aumentar el límite de alineación clang eliminó la penalización de rendimiento. Eso realmente solucionó una serie de rarezas de rendimiento que encontré. Gracias, aprendí algo nuevo.
Como ya se dijo en los comentarios, el problema es causado por el hecho de que gcc inlines es más agresivo que clang. Si hacemos clang inline muy agresivamente, el efecto desaparece:
Compilando tu código con rendimientos g++ -O3
runtime_int_default: 3000.32
runtime_int_mt: 3112.11
runtime_int_mt_64: 3069.48
runtime_float_default: 859.14
runtime_float_mt: 1027.05
runtime_float_mt_64: 1777.48
mientras clang++ -O3 -mllvm -inline-threshold=10000
yields
runtime_int_default: 3623.89
runtime_int_mt: 751.484
runtime_int_mt_64: 751.132
runtime_float_default: 1072.53
runtime_float_mt: 968.967
runtime_float_mt_64: 1781.34
Aparentemente, ahora clang out-inlines gcc en los casos int_mt
, pero todos los otros tiempos de ejecución están ahora en el mismo orden de magnitud. Usé gcc 4.8.3 y clang 3.4 en Fedora 20 64 bit.