repetir rango randomize numeros generar dev como aleatorios c++ thread-safety c++11 openmp grand-central-dispatch

c++ - rango - randomize en c



C++ 11 Seguridad del hilo de los generadores de nĂºmeros aleatorios (3)

En C ++ 11 hay un grupo de nuevos motores de generador de números Aleatorios y funciones de distribución. ¿Son hilos seguros? Si comparte una sola distribución aleatoria y un motor entre varios hilos, ¿es seguro y seguirá recibiendo números aleatorios? El escenario que estoy buscando es algo así como

void foo() { std::mt19937_64 engine(static_cast<uint64_t> (system_clock::to_time_t(system_clock::now()))); std::uniform_real_distribution<double> zeroToOne(0.0, 1.0); #pragma omp parallel for for (int i = 0; i < 1000; i++) { double a = zeroToOne(engine); } }

utilizando OpenMP o

void foo() { std::mt19937_64 engine(static_cast<uint64_t> (system_clock::to_time_t(system_clock::now()))); std::uniform_real_distribution<double> zeroToOne(0.0, 1.0); dispatch_apply(1000, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) { double a = zeroToOne(engine); }); }

utilizando libdispatch.


El estándar (bueno, N3242 ) parece no hacer mención de que la generación de números aleatorios está libre de carreras (excepto que rand no lo es), por lo que no lo es (a menos que me haya perdido algo). Además, realmente no tiene sentido tenerlos guardados, ya que incurriría en una sobrecarga relativamente alta (al menos en comparación con la generación de los números en sí), sin realmente ganar nada.

Además, realmente no veo un beneficio og que tenga un generador de números aleatorios compartido, en lugar de tener uno por hilo, cada uno de los cuales se inicializa de forma ligeramente diferente (p. Ej., A partir de los resultados de otro generador, o la identificación del hilo actual). Después de todo, probablemente no confíe en que el generador genere una secuencia determinada en cada ejecución de todos modos. Así que reescribiría su código como algo así (para openmp , no libdispatch idea de libdispatch ):

void foo() { #pragma omp parallel { //just an example, not sure if that is a good way too seed the generation //but the principle should be clear std::mt19937_64 engine((omp_get_thread_num() + 1) * static_cast<uint64_t>(system_clock::to_time_t(system_clock::now()))); std::uniform_real_distribution<double> zeroToOne(0.0, 1.0); #pragma omp for for (int i = 0; i < 1000; i++) { double a = zeroToOne(engine); } } }


La documentation no hace mención de la seguridad de los hilos, por lo que supongo que no son seguros para los hilos.


La biblioteca estándar de C ++ 11 es ampliamente segura para subprocesos. Las garantías de seguridad de subprocesos en objetos PRNG son las mismas que en los contenedores. Más específicamente, dado que las clases de PRNG son todas pseudo- aleatorias, es decir, generan una secuencia determinista basada en un estado actual definido, realmente no hay espacio para mirar o asomarse a nada fuera del estado contenido (que también es visible para el usuario). ).

Al igual que los contenedores necesitan cerraduras para que sean seguros de compartir, usted tendría que bloquear el objeto PRNG. Esto lo haría lento y no determinista. Un objeto por hilo sería mejor.

§17.6.5.9 [res.on.data.races]:

1 Esta sección especifica los requisitos que deben cumplir las implementaciones para evitar las carreras de datos (1.10). Cada función de biblioteca estándar deberá cumplir cada requisito a menos que se especifique lo contrario. Las implementaciones pueden evitar carreras de datos en casos distintos a los especificados a continuación.

2 Una función de biblioteca estándar de C ++ no accederá directa o indirectamente a objetos (1.10) accesibles por hilos que no sean el hilo actual a menos que se acceda a los objetos directa o indirectamente a través de los argumentos de la función, incluido este.

3 Una función de biblioteca estándar de C ++ no modificará directa o indirectamente los objetos (1.10) accesibles por hilos que no sean el hilo actual a menos que se acceda a los objetos directa o indirectamente mediante los argumentos no const de la función, incluido esto.

4 [Nota: Esto significa, por ejemplo, que las implementaciones no pueden usar un objeto estático para fines internos sin sincronización porque podría causar una carrera de datos incluso en programas que no comparten objetos explícitamente entre hilos. —Enuncia]

5 Una función de biblioteca estándar de C ++ no accederá a objetos accesibles indirectamente a través de sus argumentos o a través de elementos de sus argumentos de contenedor, excepto mediante la invocación de funciones requeridas por su especificación en esos elementos de contenedor.

6 Las operaciones en los iteradores obtenidos llamando a un contenedor de biblioteca estándar o una función miembro de cadena pueden acceder al contenedor subyacente, pero no deben modificarlo. [Nota: En particular, las operaciones de contenedor que invalidan los iteradores entran en conflicto con las operaciones en los iteradores asociados con ese contenedor. - nota final]

7 Las implementaciones pueden compartir sus propios objetos internos entre hilos si los objetos no son visibles para los usuarios y están protegidos contra las carreras de datos.

8 A menos que se especifique lo contrario, las funciones de la biblioteca estándar de C ++ deberán realizar todas las operaciones únicamente dentro del hilo actual si esas operaciones tienen efectos que son visibles (1.10) para los usuarios.

9 [Nota: Esto permite que las implementaciones paralelicen las operaciones si no hay efectos secundarios visibles. - nota final]