2011-10-17 10 views
7

Ho diversi thread in esecuzione contemporaneamente e ognuno di essi deve generare numeri casuali. Voglio capire se c'è un modello da seguire, capire se è corretto inizializzare il generatore casuale con srand nel thread principale o se ogni thread deve inizializzare il proprio generatore casuale. Sembra che rand/srand non siano stati progettati per essere usati con i thread e mi chiedo come posso gestire insieme thread e numeri casuali. GrazieQual è il modo più corretto per generare numeri casuali in C con pthread

MODIFICA: Ho bisogno di numeri casuali puri, ma sono anche interessato a generare una sequenza deterministica a scopo di test. Sono su Linux, ma preferisco scrivere codice il più portatile possibile.

+2

Vuoi che la sequenza sia deterministica o no? - http://stackoverflow.com/questions/6467585/deterministic-random-number-generator-tied-to-instance-thread-independent/6467623#6467623 – Flexo

+1

Se non si dispone di requisiti speciali, utilizzare 'rand_r'. –

+1

rand() è thread-safe su linux ma non è richiesto che sia così da posix, sebbene posix fornisca rand_r per quello scopo. glibc usa un mutex interno per il suo rand() - che potrebbe causare contesa se i tuoi thread generano un sacco di numeri casuali – nos

risposta

7

Su Linux è possibile utilizzare rand_r() per un generatore mediocre o la funzione drand48_r() per una molto migliore. Entrambi sono sostituzioni thread-safe per rand() e drand48(), prendendo un singolo argomento costituito dallo stato corrente, invece di utilizzare lo stato globale.

Riguardo alla domanda sull'inizializzazione, entrambi i generatori di cui sopra consentono di seminare in qualsiasi punto desideri, in modo da non essere obbligati a seminarli prima di generare i fili.

+1

rand_r è obsoleto dal POSIX 2008. Non so perché, ma mi piacerebbe. –

0

Su Windows è possibile utilizzare la funzione rand_s(), che è thread-safe. Se stai già utilizzando Boost, allora il boost::random è competente (anche se apprezzo che questo sia taggato C, non C++).

4

Quando si lavora con le discussioni e fare ad es. simulazioni o così è molto importante avere i generatori casuali indipendenti. Innanzitutto, le dipendenze tra di loro possono realmente condizionare i risultati e quindi i meccanismi per il controllo dell'accesso allo stato del generatore casuale molto probabilmente rallenteranno l'esecuzione.

Sui sistemi POSIX (dove ti sembra di essere) c'è la *rand48 famiglia di funzioni in cui erand48, nrand48 e jrand48 prendono lo stato del generatore casuale come valori di input. Quindi puoi facilmente avere stati indipendenti in ogni thread. Quelle che puoi inizializzare con un numero noto (ad esempio il numero del tuo thread) e avresti una sequenza riproducibile di numeri casuali. O lo si inizializza con qualcosa di non prevedibile come l'ora corrente & il numero per avere sequenze che variano per ogni esecuzione.

1

rand_r è thread-safe ma è anche rientranti.

Il codice sottostante genera numeri casuali uint128_t pseuso utilizzando l'algoritmo xorshift.

proprietà addizionali:

  • condiviso-rientranti
  • senza blocchi
  • thread-safe
  • ultraveloci
  • seminato da due fonti varianti di entropia

uintx_types.h:

#ifndef UINTX_TYPES_H_INCLUDED 
#define UINTX_TYPES_H_INCLUDED 

#include <inttypes.h> 
#include <ctype.h> 

typedef __uint128_t  uint128_t; 
typedef __uint64_t  uint64_t; 

#define UINT128_C(hi, lo) (((uint128_t)(hi) << 64) | (uint128_t)(lo)) 
#define UINT128_MIN   UINT128_C(0x0000000000000000, 0x0000000000000000) 
#define UINT128_0   UINT128_MIN 
#define UINT128_MAX   (~(UINT128_0) - 1) 

#endif // UINTX_TYPES_H_INCLUDED 

lf.h:

#ifndef LF_H_INCLUDED 
#define LF_H_INCLUDED 

#define AAF(ADDR, VAL)   __sync_add_and_fetch((ADDR), (VAL)) 

#endif // LF_H_INCLUDED 

rand.h:

#ifndef RAND_H_INCLUDED 
#define RAND_H_INCLUDED 

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <time.h> 
#include <limits.h> 
#include <fcntl.h> 
#include <sys/types.h> 
#include <unistd.h> 

#include "lf.h" 
#include "uintx_types.h" 


#define URANDOM  "/dev/random" 

void  srand_init(void); 
uint128_t rand_range_128(uint128_t min, uint128_t max); 

#endif // RAND_H_INCLUDED 

rand.c:

#include "rand.h" 

uint64_t r[2]; 

uint64_t xorshift64star(int index) 
{ 
    uint64_t x; 

    x = r[index]; 
    x ^= x >> 12; // a 
    x ^= x << 25; // b 
    x ^= x >> 27; // c 
    x = x * UINT64_C(2685821657736338717); 
    return AAF(&r[index], x); 
} 

void srand_init(void) 
{ 
    struct timespec ts; 
    size_t   nbytes; 
    ssize_t   bytes_read; 
    int    fd; 

    clock_gettime(CLOCK_REALTIME, &ts); 
    r[0] = (uint64_t)(ts.tv_sec * 1.0e9 + ts.tv_nsec); 
    xorshift64star(0); 

    if ((fd = open(URANDOM, O_RDONLY, S_IRUSR | S_IRGRP | S_IROTH)) == -1) 
    { 
     r[1] = r[0] + 1; 
     xorshift64star(1); 
    } 
    else 
    { 
     nbytes = sizeof(r[1]); 
     bytes_read = read(fd, &r[1], nbytes); 
     if ((bytes_read == 0) || (r[1] == 0ull)) 
     { 
      r[1] = r[0] + 1; 
      xorshift64star(1); 
     } 
     close(fd); 
    } 
} 

uint64_t rand_64(void) 
{ 
    return xorshift64star(0); 
} 

uint128_t rand_128(void) 
{ 
    uint128_t  r; 

    r = xorshift64star(0); 
    r = (r << 64) | xorshift64star(1); 
    return r; 
} 


uint128_t rand_range_128(uint128_t min, uint128_t max) 
{ 
    return (rand_128() % (max+1-min))+min; 
} 

test.c:

#define KEYS 1000 

int main(int argc, char **argv) 
{ 
    int    i; 
    uint128_t  key; 

    srand_init(); 

    for(i = 0; i <= KEYS; i++) 
    { 
     key = rand_range_128(UINT128_MIN, UINT128_MAX); 
     printf("%016"PRIx64"%016"PRIx64"\n", (uint64_t)(key >> 64), (uint64_t)key); 

    } 
    return 0; 
} 

Compilare con gcc (4.9.2) sotto Linux.

+0

Scusate per la "necropostazione" qui, ma volevo chiedere se potevo modificare srand_init (void) in srand_init (unsigned int * seed) e invece di clock_gettime (CLOCK_REALTIME, &ts); r [0] = (uint64_t) (ts .tv_sec * 1.0e9 + ts.tv_nsec); semplicemente metti r [0] = uint64_t (* seed), quindi posso sempre scegliere come inizializzare il mio rng. –

+0

Inoltre, come dovrei inizializzare questo rng in un modo sicuro? Creo i miei thread e poi cambio il seed e usando srand_init (seed) in ogni thread? O posso tranquillamente chiamare rand_range_128 (UINT128_MIN, UINT128_MAX) nella mia area parallelizzata? –

0

Su sistemi Linux è possibile utilizzare una funzione come:

size_t random_between_range(size_t min, size_t max){ 
    unsigned short state[3]; 
    unsigned int seed = time(NULL) + (unsigned int) pthread_self(); 
    memcpy(state, &seed, sizeof(seed)); 
    return min + nrand48(state) % (max - min); 
} 

Qui devo dire che Io non so se i numeri generati da questa funzione in forma per la distribuzione normale in altre parole, se questa funzione è un RNG valido nell'intervallo (min, max) ma almeno ha funzionato per me per scrivere un benchmark semplice che richiedesse alcuni numeri casuali.

Come si può vedere, la funzione utilizza l'ID del filo POSIX per riorganizzare il seme casuale. In questo modo, ogni thread ha il proprio seme casuale invece di utilizzare lo stato globale in base a time(NULL)

Problemi correlati