Почему в моем случае многопоточность медленнее, чем последовательное программирование?

Я новичок в многопоточности и пытаюсь изучить ее с помощью простой программы, которая прибавляет 1 к n и возвращает сумму. В последовательном случае mainдважды вызывает функцию sumFrom1для n = 1e5 и 2e5; в многопоточных случаях два потока создаются с помощью pthread_createи две суммы вычисляются в отдельном потоке. Многопоточная версия намного медленнее, чем последовательная версия (, см. результаты ниже ). Я запускаю это на платформе с 12 -ЦП, и между потоками нет связи.

Многопоточная:

Thread 1 returns: 0 
Thread 2 returns: 0 
sum of 1..10000: 50005000
sum of 1..20000: 200010000
time: 156 seconds

Последовательная:

sum of 1..10000: 50005000
sum of 1..20000: 200010000
time: 56 seconds

Когда я добавляю-O2 в компиляцию, время многопоточной версии (9 с )меньше, чем у последовательной версии (11 с ), но не так много, как я ожидаю. Я всегда могу установить флаг-O2 , но меня интересует низкая скорость многопоточности в неоптимизированном случае. Должна ли она быть медленнее, чем последовательная версия? Если нет, что я могу сделать, чтобы сделать это быстрее?

Код:

#include <stdio.h>
#include <pthread.h>
#include <time.h>

typedef struct my_struct
{
  int n;                                                                                                                                                              
  int sum;                                                                                                                                                            
}my_struct_t;                                                                                                                                                         

void *sumFrom1(void* sit)                                                                                                                                              
{                                                                                                                                                                     
  my_struct_t* local_sit = (my_struct_t*) sit;                                                                                                                          
  int i;                                                                                                                                                              
  int nsim = 500000;  // Loops for consuming time                                                                                                                                                
  int j;                                                                                                                                                              

  for(j = 0; j < nsim; j++)                                                                                                                                           
  {                                                                                                                                                                   
    local_sit->sum = 0;                                                                                                                                                
    for(i = 0; i <= local_sit->n; i++)                                                                                                                                 
      local_sit->sum += i;                                                                                                                                             
  }    
}

int main(int argc, char *argv[])                                                                                                                                      
{                                                                                                                                                                     
  pthread_t    thread1;                                                                                                                                               
  pthread_t    thread2;                                                                                                                                               
  my_struct_t  si1;                                                                                                                                                   
  my_struct_t  si2;                                                                                                                                                   
  int          iret1;                                                                                                                                                 
  int          iret2;                                                                                                                                                 
  time_t       t1;                                                                                                                                                    
  time_t       t2;                                                                                                                                                    


  si1.n = 10000;                                                                                                                                                      
  si2.n = 20000;                                                                                                                                                      

  if(argc == 2 && atoi(argv[1]) == 1) // Use "./prog 1" to test the time of multithreaded version                                                                                                                                
  {                                                                                                                                                                   
    t1 = time(0);                                                                                                                                                     
    iret1 = pthread_create(&thread1, NULL, sumFrom1, (void*)&si1);      
    iret2 = pthread_create(&thread2, NULL, sumFrom1, (void*)&si2);                                                                                                     
    pthread_join(thread1, NULL);                                                                                                                                      
    pthread_join(thread2, NULL);                                                                                                                                      
    t2 = time(0);                                                                                                                                                     

    printf("Thread 1 returns: %d\n",iret1);                                                                                                                           
    printf("Thread 2 returns: %d\n",iret2);                                                                                                                           
    printf("sum of 1..%d: %d\n", si1.n, si1.sum);                                                                                                                     
    printf("sum of 1..%d: %d\n", si2.n, si2.sum);                                                                                                                     
    printf("time: %d seconds", t2 - t1);                                                                                                                              

  }                                                                                                                                                                   
  else     // Use "./prog" to test the time of sequential version                                                                                                                                                           
  {                                                                                                                                                                   
    t1 = time(0);                                                                                                                                                     
    sumFrom1((void*)&si1);                                                                                                                                            
    sumFrom1((void*)&si2);                                                                                                                                            
    t2 = time(0);                                                                                                                                                     

    printf("sum of 1..%d: %d\n", si1.n, si1.sum);                                                                                                                     
    printf("sum of 1..%d: %d\n", si2.n, si2.sum);                                                                                                                     
    printf("time: %d seconds", t2 - t1); 
  }                                                                                             
  return 0;                                                                                         
}   

ОБНОВЛЕНИЕ 1:

Немного погуглив на тему «ложный обмен» (Спасибо, @Martin James! ), я думаю, что это основная причина. Есть (как минимум )два способа это исправить:

Первый способ — вставить буферную зону между двумя структурами (Спасибо, @dasblinkenlight):

my_struct_t  si1;
char         memHolder[4096];
my_struct_t  si2; 

Без-O2 , затраты времени уменьшаются с ~156 с до ~38 с.

Второй способ — избегать частого обновления sit->sum, что можно реализовать с помощью переменной temp в sumFrom1(, как ответил @Jens Gustedt):

for(int sum = 0, j = 0; j < nsim; j++)              
{
  sum = 0;
  for(i = 0; i <= local_sit->n; i++)
    sum += i;
}
local_sit->sum = sum;

Без-O2 затраты времени уменьшаются с ~156 с до ~35 с или ~109 с (У него два пика! Я не знаю почему. ). При-O2 время остается ~8 с.

6
задан cogitovita 11 April 2012 в 10:38
поделиться