multithreadingrandom_r比单线程版本慢

以下程序基本上与此处所述的程序相同。 当我使用两个线程(NTHREADS == 2)运行和编译程序时,我得到以下运行时间:

real 0m14.120s user 0m25.570s sys 0m0.050s 

当它只用一个线程(NTHREADS == 1)运行时,即使只使用一个内核,运行时间也会显着提高。

 real 0m4.705s user 0m4.660s sys 0m0.010s 

我的系统是双核的,我知道random_r是线程安全的,我很确定它是非阻塞的。 当同样的程序在没有random_r的情况下运行并且余弦和正弦的计算被用作replace时,双线程版本运行的时间大约是预期的1/2。

 #include <pthread.h> #include <stdlib.h> #include <stdio.h> #define NTHREADS 2 #define PRNG_BUFSZ 8 #define ITERATIONS 1000000000 void* thread_run(void* arg) { int r1, i, totalIterations = ITERATIONS / NTHREADS; for (i = 0; i < totalIterations; i++){ random_r((struct random_data*)arg, &r1); } printf("%i\n", r1); } int main(int argc, char** argv) { struct random_data* rand_states = (struct random_data*)calloc(NTHREADS, sizeof(struct random_data)); char* rand_statebufs = (char*)calloc(NTHREADS, PRNG_BUFSZ); pthread_t* thread_ids; int t = 0; thread_ids = (pthread_t*)calloc(NTHREADS, sizeof(pthread_t)); /* create threads */ for (t = 0; t < NTHREADS; t++) { initstate_r(random(), &rand_statebufs[t], PRNG_BUFSZ, &rand_states[t]); pthread_create(&thread_ids[t], NULL, &thread_run, &rand_states[t]); } for (t = 0; t < NTHREADS; t++) { pthread_join(thread_ids[t], NULL); } free(thread_ids); free(rand_states); free(rand_statebufs); } 

我很困惑,为什么在生成随机数字时,两个线程版本比单线程版本差得多,考虑random_r是为了在multithreading应用程序中使用。

Solutions Collecting From Web of "multithreadingrandom_r比单线程版本慢"

内存中的数据空间非常简单:

 struct random_data* rand_states = (struct random_data*)calloc(NTHREADS * 64, sizeof(struct random_data)); char* rand_statebufs = (char*)calloc(NTHREADS*64, PRNG_BUFSZ); pthread_t* thread_ids; int t = 0; thread_ids = (pthread_t*)calloc(NTHREADS, sizeof(pthread_t)); /* create threads */ for (t = 0; t < NTHREADS; t++) { initstate_r(random(), &rand_statebufs[t*64], PRNG_BUFSZ, &rand_states[t*64]); pthread_create(&thread_ids[t], NULL, &thread_run, &rand_states[t*64]); } 

导致我的双核心机器运行时间更快。

这将确认怀疑它是要测试 – 你是在两个单独的线程在同一缓存线上的变异值,所以有缓存争用。 Herb Sutter的“机器架构 – 你的编程语言从来没有告诉过你的话”值得关注,如果你还没有掌握这些知识,那么他会在1:20左右开始虚假分享。

计算出你的缓存行大小,并创建每个线程的数据,使之与它对齐。

把所有的线程数据都放到一个结构体中,把它清理一下,

 #define CACHE_LINE_SIZE 64 struct thread_data { struct random_data random_data; char statebuf[PRNG_BUFSZ]; char padding[CACHE_LINE_SIZE - sizeof ( struct random_data )-PRNG_BUFSZ]; }; int main ( int argc, char** argv ) { printf ( "%zd\n", sizeof ( struct thread_data ) ); void* apointer; if ( posix_memalign ( &apointer, sizeof ( struct thread_data ), NTHREADS * sizeof ( struct thread_data ) ) ) exit ( 1 ); struct thread_data* thread_states = apointer; memset ( apointer, 0, NTHREADS * sizeof ( struct thread_data ) ); pthread_t* thread_ids; int t = 0; thread_ids = ( pthread_t* ) calloc ( NTHREADS, sizeof ( pthread_t ) ); /* create threads */ for ( t = 0; t < NTHREADS; t++ ) { initstate_r ( random(), thread_states[t].statebuf, PRNG_BUFSZ, &thread_states[t].random_data ); pthread_create ( &thread_ids[t], NULL, &thread_run, &thread_states[t].random_data ); } for ( t = 0; t < NTHREADS; t++ ) { pthread_join ( thread_ids[t], NULL ); } free ( thread_ids ); free ( thread_states ); } 

CACHE_LINE_SIZE 64:

 refugio:$ gcc -O3 -o bin/nixuz_random_r src/nixuz_random_r.c -lpthread refugio:$ time bin/nixuz_random_r 64 63499495 944240966 real 0m1.278s user 0m2.540s sys 0m0.000s 

或者你可以使用两倍的缓存行大小,并使用malloc – 额外的填充确保变异的内存是分开的行,因为malloc是16(IIRC),而不是64字节对齐。

(我把ITERS减少了十倍,而不是一个愚蠢的快速机器)

我不知道这是否是相关的 – 但我只是看到了一个非常相似的行为(2线程比一个慢几个数量级)…我基本上改变了:

  srand(seed); foo = rand(); 

到一个

  myseed = seed; foo = rand_r(&myseed); 

并“固定”它(2线程现在可靠几乎两倍 – 例如19s而不是35s)。

我不知道是什么问题 – 在rand()的内部锁定或缓存一致性可能? 无论如何,还有一个random_r()所以也许这对你(一年前)或其他人有用。