2012-04-11 5 views
6

Я новичок в многопоточности и пытаюсь изучить его с помощью простой программы, которая добавляет 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 

Sequential:

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

Когда я добавляю -O2 в компиляции, время многопоточной версии (9s) меньше, чем последовательной версии (11s) , но не так много, как я ожидаю. У меня всегда есть флаг -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;                       
} 

Update1:

После небольшого Googling на "ложном обмена" (Спасибо, @Martin Джеймс!), Я думаю, что это является основной причиной. Есть (по крайней мере) два способа это исправить:

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

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

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

Второй способ избежать часто обновлений sit->sum, который может быть реализован с использованием переменного Темпа в 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, то много времени уменьшается от ~ От 156s до ~ 35s или ~ 109s (у этого есть два пика! Я не знаю почему.). С -O2 длительное время остается ~ 8 с.

+0

В таких тестах нам нужно усреднить результаты. Сколько раз вы запускали тесты с оптимизацией -O2? И если вы запускали несколько раз, то что такое avg раз? –

+2

si1 и si2 находятся рядом друг с другом. Ложное разделение? –

+0

@PavanManjunath Спасибо за совет. Я побежал 10 раз с -O2. время avg составляет 7.9s для многопоточной версии и 11.7 для последовательного. Флуктуация мала. – cogitovita

ответ

3

Изменяя свой код

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

void *sumFrom1(void* sit) 
{ 
    my_struct_t* local_sit = sit; 
    size_t nsim = 500000; // Loops for consuming time 
    size_t n = local_sit->n; 
    size_t sum = 0; 
    for(size_t j = 0; j < nsim; j++) 
    { 
    for(size_t i = 0; i <= n; i++) 
     sum += i; 
    } 
    local_sit->sum = sum; 
    return 0; 
} 

явление исчезает. Проблемы, которые у вас были:

  • с использованием int, поскольку тип данных совершенно не подходит для такого теста. Ваши цифры, где такая сумма переполнена. Переполнение подписанных типов - это неопределенное поведение. Вам повезло, что он не ест ваш обед.
  • с ограничениями и суммирующими переменными с косвенным порядком покупает вас дополнительные грузы и магазины, которые в случае -O0 действительно выполнены как , причем все последствия ложного обмена и тому подобное.

Ваш код также наблюдали другие ошибки:

  • недостающий включают atoi
  • superflouous литых и из void*
  • печати time_t в int

Пожалуйста компилировать ваш код с -Wall до pos тин.

+0

Использование 'size_t sum = 0;' приводит к значительному усилению форматирования. Затем добавление этого параметра 'size_t n = local_sit-> n;' снова замедляет работу. Любые идеи, почему? (Все скомпилировано с -O0) – alk

+1

Нет, не совсем. Обсуждение неоптимизированного кода на эту деталь не имеет большого смысла, я думаю. Если вы хотите знать, что происходит на самом деле, первым шагом будет загляните в ассемблер, созданный с помощью '-S'. –

Смежные вопросы