代码之家  ›  专栏  ›  技术社区  ›  Nixuz

多线程随机\r比单线程版本慢

  •  10
  • Nixuz  · 技术社区  · 15 年前

    以下程序与所描述的程序基本相同 here . 当我使用两个线程(nthreads==2)运行和编译程序时,我得到以下运行时间:

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

    当它只使用一个线程(nthreads==1)运行时,即使它只使用一个核心,运行时间也会明显地更好。

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

    我的系统是双核的,我知道Random_r是线程安全的,我很确定它是非阻塞的。当同一个程序在没有随机变量的情况下运行,并使用余弦和正弦的计算作为替换时,双线程版本的运行时间约为预期的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);
    }
    

    我很困惑为什么在生成随机数时,双线程版本的性能要比单线程版本差得多,因为在多线程应用程序中应该使用随机变量。

    2 回复  |  直到 11 年前
        1
  •  13
  •   Pete Kirkham    11 年前

    对内存中的数据进行了一个非常简单的空间更改:

    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]);
    }
    

    在我的双核机器上运行时间更快。

    这将证实它打算测试的怀疑——在两个单独的线程中,您正在改变同一缓存线上的值,因此存在缓存争用。药草萨特 'machine architecture - what your programming language never told you' talk 如果你有时间的话值得一看,如果你还不知道的话,他会在1:20左右开始演示错误的分享。

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

    将线程的所有数据plock到一个结构中,并将其对齐,这样做更简单:

    #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字节对齐的。

    (我将迭代次数减少了10倍,而不是拥有一台愚蠢的快速机器)

        2
  •  1
  •   Tisho DadoCe    12 年前

    我不知道这是否相关-但我看到了一个非常相似的行为(两个线程的速度比一个线程慢很多)。我基本上改变了:

      srand(seed);
      foo = rand();
    

    到A

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

    并且“固定”了它(现在2个线程的速度几乎是原来的两倍——例如19秒而不是35秒)。

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