首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么n个线程的平均速度不如C中的单个线程那么快?

为什么n个线程的平均速度不如C中的单个线程那么快?
EN

Stack Overflow用户
提问于 2016-06-25 21:54:02
回答 2查看 108关注 0票数 0

我编写了一个程序,其中有两个线程在做同样的事情,但是我发现每个线程的吞吐量都比我只生成一个线程要慢。然后我编写了这个简单的测试,看看这是我的问题还是系统的问题。

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>


/*
 * Function: run_add
 * -----------------------
 * Do addition operation for iteration ^ 3 times
 *
 * returns: void
 */
void *run_add(void *ptr) {
  clock_t t1, t2;
  t1 = clock();

  int sum = 0;
  int i = 0, j = 0, k = 0;
  int iteration = 1000;
  long total = iteration * iteration * iteration;
  for (i = 0; i < iteration; i++) {
    for (j = 0; j < iteration; j++) {
      for (k = 0; k < iteration; k++) {
        sum++;
      }
    }
  }

  t2 = clock();
  float diff = ((float)(t2 - t1) / 1000000.0F );
  printf("thread id = %d\n", (int)(pthread_self()));
  printf("Total addtions: %ld\n", total);
  printf("Total time: %f second\n", diff);
  printf("Addition per second: %f\n", total / diff);
  printf("\n");

  return NULL;
}


void run_test(int num_thread) {
  pthread_t pth_arr[num_thread];
  int i = 0;
  for (i = 0; i < num_thread; i++) {
    pthread_create(&pth_arr[i], NULL, run_add, NULL);
  }

  for (i = 0; i < num_thread; i++) {
    pthread_join(pth_arr[i], NULL);
  }
}

int main() {
  int num_thread = 5;
  int i = 0;
  for (i = 1; i < num_thread; i++) {
    printf("Running SUM with %d threads. \n\n", i);
    run_test(i);
  }
  return 0;
}

结果表明,n个线程的平均速度比单个线程慢。我拥有的线程越多,每个线程就越慢。

结果如下:

用一个线程运行SUM。 线程id = 528384,总加法: 1000000000,总时间: 1.441257秒,每秒加法: 693838784.000000 用两个线程运行SUM。 线程id = 528384,总加法: 1000000000,总时间: 2.970870秒,每秒加法: 336601728.000000 线程id = 1064960,总加法: 1000000000,总时间: 2.972992秒,每秒加法: 336361504.000000 用3个线程运行SUM。 线程id = 1064960,总加法: 1000000000,总时间: 4.434701秒,每秒加法: 225494352.000000 线程id = 1601536,总加法: 1000000000,总时间: 4.449250秒,每秒加法: 224756976.000000 线程id = 528384,总加法: 1000000000,总时间: 4.454826秒,每秒加法: 224475664.000000 用4个线程运行SUM。 线程id = 528384,总加法: 1000000000,总时间: 6.261967秒,每秒加法: 159694224.000000 线程id = 1064960,总加法: 1000000000,总时间: 6.293107秒,每秒加法: 158904016.000000 线程id = 2138112,总加法: 1000000000,总时间: 6.295047秒,每秒加法: 158855056.000000 线程id = 1601536,总加法: 1000000000,总时间: 6.306261秒,每秒加法: 158572560.000000

我有一个4核CPU,我的系统监视器显示,每次我运行n个线程时,n个CPU核心是100%被利用的。N个线程(<=我的CPU内核)应该运行n倍于一个线程,这是真的吗?为何这里不是这样呢?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-06-25 22:03:12

clock()测量的是CPU时间而不是“墙”时间。它还测量所有线程的总时间。

CPU时间是处理器执行代码时的时间,墙时间是实际运行的时间(就像墙上的时钟显示的那样)。

使用/usr/bin/time为您的程序计时,以查看到底发生了什么。或者使用像time()、gettimeofday()或clock_gettime()这样的墙壁时间函数。

clock_gettime()可以测量此线程的CPU时间、此进程的CPU时间或壁时间。-这可能是做这种实验的最好方法。

票数 6
EN

Stack Overflow用户

发布于 2016-06-26 01:39:40

虽然你有你的答案,为什么多线程性能似乎比单线程差,你可以做几件事来清理你的程序的逻辑,使它像你想要的那样工作。

首先,如果您跟踪相对过去的墙壁时间和clock()时间的差异所报告的时间,您就会注意到报告的时间大约是实际墙时间的(n-处理器核心)的倍数。另一个答案解释了这一点。

对于相对每核性能计时,clock()的使用是很好的。你只得到了墙时间的近似值,但是对于每秒的相对加法,这提供了一个清晰的每核心性能观察。

虽然您正确地将1000000除数用于diff,但是time.h为您提供了一个方便的define。POSIX要求CLOCKS_PER_SEC等于与实际分辨率无关的1000000。这个常数是在time.h中提供的。

接下来,您还应该注意到,直到所有线程连接起来之后,才会报告每个核心的输出,从而使run_add中的报告总数变得毫无意义。为了方便起见,您可以从各个线程输出thread_id等,但是在所有线程被连接之后,应该在调用函数中计算回时间信息。这将显着地清理run_add的逻辑。此外,如果您希望能够更改迭代次数,则应该考虑通过ptr传递该值。例如:

代码语言:javascript
复制
/*
 * Function: run_add
 * -----------------------
 * Do addition operation for iteration ^ 3 times
 *
 * returns: void
 */
void *run_add (void *ptr)
{
    int i = 0, j = 0, k = 0, iteration = *(int *)ptr;
    unsigned long sum = 0;

    for (i = 0; i < iteration; i++)
        for (j = 0; j < iteration; j++)
            for (k = 0; k < iteration; k++)
                sum++;

    printf ("  thread id  = %lu\n", (long unsigned) (pthread_self ()));
    printf ("  iterations = %lu\n\n", sum);

    return NULL;
}

run_test相对没有变化,大部分的计算更改都是从run_add迁移到main,并被缩放到所使用的核心数量。下面是对main的重写,允许用户指定用作第一个参数的核心数量(默认情况下使用all-cores ),以及作为第二个参数的立方迭代次数的基(默认情况下是1000):

代码语言:javascript
复制
int main (int argc, char **argv) {

    int nproc = sysconf (_SC_NPROCESSORS_ONLN), /* number of core available */
        num_thread = argc > 1 ? atoi (argv[1]) : nproc,
        iter = argc > 2 ? atoi (argv[2]) : 1000;
    unsigned long subtotal = iter * iter * iter,
        total = subtotal * num_thread;
    double diff = 0.0, t1 = 0.0, t2 = 0.0;

    if (num_thread > nproc) num_thread = nproc;
    printf ("\nrunning sum with %d threads.\n\n", num_thread);

    t1 = clock ();
    run_test (num_thread, &iter);
    t2 = clock ();
    diff = (double)((t2 - t1) / CLOCKS_PER_SEC / num_thread);

    printf ("----------------\nTotal time: %lf second\n", diff);
    printf ("Total addtions: %lu\n", total);
    printf ("Additions per-second: %lf\n\n", total / diff);

    return 0;
}

将所有的部分放在一起,您可以编写一个工作示例如下所示。确保禁用优化以防止编译器为sum优化循环,等等.

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>

/*
 * Function: run_add
 * -----------------------
 * Do addition operation for iteration ^ 3 times
 *
 * returns: void
 */
void *run_add (void *ptr)
{
    int i = 0, j = 0, k = 0, iteration = *(int *)ptr;
    unsigned long sum = 0;

    for (i = 0; i < iteration; i++)
        for (j = 0; j < iteration; j++)
            for (k = 0; k < iteration; k++)
                sum++;

    printf ("  thread id  = %lu\n", (long unsigned) (pthread_self ()));
    printf ("  iterations = %lu\n\n", sum);

    return NULL;
}

void run_test (int num_thread, int *it)
{
    pthread_t pth_arr[num_thread];
    int i = 0;

    for (i = 0; i < num_thread; i++)
        pthread_create (&pth_arr[i], NULL, run_add, it);

    for (i = 0; i < num_thread; i++)
        pthread_join (pth_arr[i], NULL);
}

int main (int argc, char **argv) {

    int nproc = sysconf (_SC_NPROCESSORS_ONLN),
        num_thread = argc > 1 ? atoi (argv[1]) : nproc,
        iter = argc > 2 ? atoi (argv[2]) : 1000;
    unsigned long subtotal = iter * iter * iter,
        total = subtotal * num_thread;
    double diff = 0.0, t1 = 0.0, t2 = 0.0;

    if (num_thread > nproc) num_thread = nproc;
    printf ("\nrunning sum with %d threads.\n\n", num_thread);

    t1 = clock ();
    run_test (num_thread, &iter);
    t2 = clock ();
    diff = (double)((t2 - t1) / CLOCKS_PER_SEC / num_thread);

    printf ("----------------\nTotal time: %lf second\n", diff);
    printf ("Total addtions: %lu\n", total);
    printf ("Additions per-second: %lf\n\n", total / diff);

    return 0;
}

示例使用/输出

现在,您可以根据使用的核数来测量每秒执行的添加的相对数量,并让它返回一个大致相当于墙壁时间的Total time。例如,使用单个核心每秒测量加法的结果如下:

代码语言:javascript
复制
$ ./bin/pthread_one_per_core 1

running sum with 1 threads.

  thread id  = 140380000397056
  iterations = 1000000000

----------------
Total time: 2.149662 second
Total addtions: 1000000000
Additions per-second: 465189411.172547

近似465M additions per-sec.使用两个核应该是这个速率的两倍:

代码语言:javascript
复制
$ ./bin/pthread_one_per_core 2

running sum with 2 threads.

  thread id  = 140437156796160
  iterations = 1000000000

  thread id  = 140437165188864
  iterations = 1000000000

----------------
Total time: 2.152436 second
Total addtions: 2000000000
Additions per-second: 929179560.000957

929M/s每秒增加的两倍。使用四核:

代码语言:javascript
复制
$ ./bin/pthread_one_per_core 4

running sum with 4 threads.

  thread id  = 139867841853184
  iterations = 1000000000

  thread id  = 139867858638592
  iterations = 1000000000

  thread id  = 139867867031296
  iterations = 1000000000

  thread id  = 139867850245888
  iterations = 1000000000

----------------
Total time: 2.202021 second
Total addtions: 4000000000
Additions per-second: 1816513309.422720

再次翻倍到1.81G/s,并使用8核提供了预期的结果:

代码语言:javascript
复制
$ ./bin/pthread_one_per_core

running sum with 8 threads.

  thread id  = 140617712838400
  iterations = 1000000000

  thread id  = 140617654089472
  iterations = 1000000000

  thread id  = 140617687660288
  iterations = 1000000000

  thread id  = 140617704445696
  iterations = 1000000000

  thread id  = 140617662482176
  iterations = 1000000000

  thread id  = 140617696052992
  iterations = 1000000000

  thread id  = 140617670874880
  iterations = 1000000000

  thread id  = 140617679267584
  iterations = 1000000000

----------------
Total time: 2.250243 second
Total addtions: 8000000000
Additions per-second: 3555171004.558562

3.55G/s。看看这两个答案(目前),并让我们知道,如果您有任何问题。

注意:还有许多可以应用的额外清理和验证,但就您的示例而言,将类型更新为rational无符号可以防止使用thread_id和加法号产生奇怪的结果。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38033295

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档