Расчет суммы с помощью параллельного программирования - PullRequest
1 голос
/ 02 августа 2020

Я хочу прибавить 1 к числу (0) 10 миллиардов раз. Я пробовал два подхода:

  1. Используйте один поток (основной поток) для выполнения работы.
  2. Создайте два потока и сделайте половину сложения в 1-м потоке, а другую половину - в второй.

Я ожидал, что второй подход займет меньше времени, чем первый, но результат напротив этого. Ниже приведены тайминги с использованием многопоточного и однопоточного подхода. (основной поток) соответственно.

real    0m35.661s    
user    1m6.652s
sys 0m0.004s

real    0m25.162s
user    0m25.162s
sys 0m0.000s

Ниже приведен исходный код.

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

static unsigned long long int sum1, sum2;
long long unsigned int l1 = 10000000000/2;
long long unsigned int l2 = 10000000000/2 + 1;  

void *thread1(void *arg)
{
    unsigned long long int i;
    printf("%s\n", __func__);
    for (i=0;i<l1; i++)
        sum1 += 1;

    pthread_exit((void *)0);
}

void *thread2(void *arg)
{
    unsigned long long int i;
    printf("%s\n", __func__);
#if 0
    /* In case of single thread, the following for loop is used */
    for (i=0;i<10000000000; i++)
        sum2 += 1;
#endif
    for (i=l2;i<10000000000; i++)
        sum2 += 1;

    pthread_exit((void *)0);
}

int main(void)
{
    pthread_t tid1, tid2;
    void *res1, *res2;
    void *(*tptr)(void *);

    printf("%llu, %llu\n", l1, l2);
    /* all pthread_* calls are disabled in single thread mode
     * only main thread used which calls -thread2- method */
    pthread_create(&tid1, NULL, &thread1, NULL);

    pthread_create(&tid2, NULL, &thread2, NULL);

    if(pthread_join(tid1, NULL))
            printf("Error joining with t1");
    if(pthread_join(tid2, NULL))
            printf("Error joining with t2");

/* Enable  for single thread mode */
#if 0
    tptr = thread2;
    tptr(NULL);
#endif
    printf("Main thread exiting\n");
    return 0;
}

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

=============== Попробовав решение, предложенное в принятом ответе, я увидел следующие показания в многопоточном случае -

real    0m12.526s
user    0m23.375s
sys 0m0.004s

, что, как и ожидалось, почти половина того, что я получаю с одним потоком.

1 Ответ

2 голосов
/ 02 августа 2020

Проблема в ложном обмене . sum1 и sum2 хранятся в одной строке кэша, поэтому их хранилища конкурируют, что приводит к сериализации.

Вы можете использовать alignas, чтобы заставить их разделять строки кеша.

alignas(64) static unsigned long long int sum1, sum2;

Это, однако, артефакт неиспользования оптимизаций. Нет смысла хранить промежуточные значения суммы в ОЗУ, они должны быть в регистре, и компилятор, вероятно, сделает это, если вы скомпилировали с включенной оптимизацией. Однако тогда это также исключило бы целые циклы, поскольку эффект повторного добавления предсказуем.

...