【问题标题】:Dividing work to more threads takes more time, why?将工作分配给更多线程需要更多时间,为什么?
【发布时间】:2015-01-20 12:01:48
【问题描述】:

我有一个小的 C 程序,它使用 monte-carlo-simulation 计算 pi,它基本上只是测试随机点 [x,y] 是在圆内还是圆外。

为了逼近 pi,我必须使用大量的样本 n,其直接比例复杂度为 O(n)。所以为了计算大量的样本n,我实现了POSIX threads api来并行计算能力。

我的代码如下所示:

pthread_t worker[nthreads]; /* creates workers for each thread */
struct param aparam[nthreads]; /* struct param{ long* hits; long rounds; }; */
long nrounds = nsamples / nthreads; /* divide samples to subsets of equal rounds per thread */

for (int i = 0; i < nthreads; ++i) { /* loop to create threads */
    aparam[i].hits = 0;
    aparam[i].rounds = nrounds;
    pthread_create(&worker[i], NULL, calc_pi, &aparam[i]); /* calls calc_pi(void* vparam){}  */ 
}

long nhits = 0;
for (int j = 0; j < nthreads; ++j) { /* collects results */
    pthread_join(worker[j], NULL);
    nhits += (long)aparam[j].hits; /* counts hits inside the cicrle */
}

这就是每个线程正在做的事情:

void* calc_pi(void* vparam)
{ /* counts hits inside a circle */
    struct param *iparam;
    iparam = (struct param *) vparam;
    long hits = 0;
    float x, y, z;
    for (long i = 0; i < iparam->rounds; ++i) {
        x = (float)rand()/RAND_MAX;
        y = (float)rand()/RAND_MAX;
        z = x * x + y * y;
        if (z <= 1.f) /* circle radius of 1 */
            ++hits;
    }
    iparam->hits = (long*)hits;
    return NULL;
}

现在我有一个奇怪的观察。使用相同的样本集 n 并且随着线程数量的增加 i,这个程序需要 更多的时间而不是更少的时间

以下是一些平均运行时间(可重现):

-------------------------------------------------
| Threads[1] | Samples[1] | Rounds[1] | Time[s] |
-------------------------------------------------
|        32  |  268435456 |   8388608 |    118  |
|        16  |  268435456 |  16777216 |    106  |
|         8  |  268435456 |  33554432 |    125  |
|         4  |  268435456 |  67108864 |    152  |
|         2  |  268435456 | 134217728 |     36  |
|         1  |  268435456 | 268435456 |     15  |
-------------------------------------------------

例如,为什么两个线程执行相同的工作所花费的时间是单个线程的两倍以上?我的假设是划分工作的两个线程应该将时间减少至少 50%。

使用 GCC 4.9.1 和以下标志编译:

gcc -O2 -std=gnu11 -pthread pipa.c -lpthread -o pipa

我的硬件是双 Intel Xeon E5520(2 个处理器,每个 4 核)@ 2.26 GHz,禁用超线程,运行 2.6.18 内核的科学 linux。

有什么想法吗?

【问题讨论】:

  • Linux 2.6.18 是古老的。像,史前。我很确定从那时起多线程程序有很多改进。例如,您使用的是哪个 pthreads 实现? LinuxThreads 还是 NPTL?
  • 你确定线程在不同的内核上运行吗?也许出于某种原因,它们共享同一个内核,因此上下文切换开销会增加运行时间
  • rand() 可能会导致争用。
  • EOF,我可以在 linux 3.17.3 上重现这个;埃里克,我用 htop 进行了检查,线程正在使用所有内核。 2501,rand()好像是问题,使用rand_r()可以提高性能。

标签: c multithreading performance pthreads multiprocessing


【解决方案1】:

您的线程执行的最昂贵的操作是调用rand()rand() 是一个幼稚、简单且通常不可扩展的函数(因为它保证相同的种子产生相同的 随机 数字序列)。我认为rand() 内部的锁正在序列化所有线程。(*)

确认是否是问题的一个简单技巧是在调试器下启动程序,然后,多次:暂停它,捕获线程的堆栈跟踪,继续。堆栈跟踪中最常出现的东西很可能是瓶颈。

(*) 更慢的原因是锁争用会导致额外的性能损失。此外,许多线程增加了进程调度和上下文切换的额外开销。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-05
    • 2019-08-21
    相关资源
    最近更新 更多