簡體   English   中英

使用多個線程時性能提升很少

[英]Little performance increasing when using multiple threads

我正在實現解決線性系統的多線程Jordan-Gauss方法,我發現在兩個線程上運行所花費的時間比在單線程上運行的時間少約15%而不是理想的50%。 所以我寫了一個復制這個的簡單程序。 在這里,我創建一個矩陣2000x2000,並為每個線程提供2000 / THREADS_NUM行,以便對它們進行一些計算。

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

#ifndef THREADS_NUM
#define THREADS_NUM 1
#endif

#define MATRIX_SIZE 2000


typedef struct {
    double *a;
    int row_length;
    int rows_number;
} TWorkerParams;

void *worker_thread(void *params_v)
{
    TWorkerParams *params = (TWorkerParams *)params_v;
    int row_length = params->row_length;
    int i, j, k;
    int rows_number = params->rows_number;
    double *a = params->a;

    for(i = 0; i < row_length; ++i) // row_length is always the same
    {
        for(j = 0; j < rows_number; ++j) // rows_number is inverse proportional
                                         // to the number of threads
        {
            for(k = i; k < row_length; ++k) // row_length is always the same
            {
                a[j*row_length + k] -= 2.;
            }
        }
    }
    return NULL;
}


int main(int argc, char *argv[])
{
    // The matrix is of size NxN
    double *a =
        (double *)malloc(MATRIX_SIZE * MATRIX_SIZE * sizeof(double));
    TWorkerParams *params =
        (TWorkerParams *)malloc(THREADS_NUM * sizeof(TWorkerParams));
    pthread_t *workers = (pthread_t *)malloc(THREADS_NUM * sizeof(pthread_t));
    struct timespec start_time, end_time;
    int rows_per_worker = MATRIX_SIZE / THREADS_NUM;
    int i;
    if(!a || !params || !workers)
    {
        fprintf(stderr, "Error allocating memory\n");
        return 1;
    }
    for(i = 0; i < MATRIX_SIZE*MATRIX_SIZE; ++i)
        a[i] = 4. * i; // just an example matrix
    // Initializtion of matrix is done, now initialize threads' params
    for(i = 0; i < THREADS_NUM; ++i)
    {
        params[i].a = a + i * rows_per_worker * MATRIX_SIZE;
        params[i].row_length = MATRIX_SIZE;
        params[i].rows_number = rows_per_worker;
    }
    // Get start time
    clock_gettime(CLOCK_MONOTONIC, &start_time);
    // Create threads
    for(i = 0; i < THREADS_NUM; ++i)
    {
        if(pthread_create(workers + i, NULL, worker_thread, params + i))
        {
            fprintf(stderr, "Error creating thread\n");
            return 1;
        }
    }
    // Join threads
    for(i = 0; i < THREADS_NUM; ++i)
    {
        if(pthread_join(workers[i], NULL))
        {
            fprintf(stderr, "Error creating thread\n");
            return 1;
        }
    }
    clock_gettime(CLOCK_MONOTONIC, &end_time);
    printf("Duration: %lf msec.\n", (end_time.tv_sec - start_time.tv_sec)*1e3 +
            (end_time.tv_nsec - start_time.tv_nsec)*1e-6);
    return 0;
}

這是我如何編譯它:

gcc threads_test.c -o threads_test1 -lrt -pthread -DTHREADS_NUM=1 -Wall -Werror -Ofast
gcc threads_test.c -o threads_test2 -lrt -pthread -DTHREADS_NUM=2 -Wall -Werror -Ofast

現在,當我跑步時,我得到:

./threads_test1
Duration: 3695.359552 msec.
./threads_test2
Duration: 3211.236612 msec.

這意味着2線程程序運行速度比單線程快13%,即使線程之間沒有同步並且它們不共享任何內存。 我找到了這個答案: https//stackoverflow.com/a/14812411/5647501並認為這可能是處理器緩存的一些問題,所以我添加了填充,但結果仍然相同。 我改變了我的代碼如下:

typedef struct {
    double *a;
    int row_length;
    int rows_number;
    volatile char padding[64 - 2*sizeof(int)-sizeof(double)];
} TWorkerParams;

#define VAR_SIZE (sizeof(int)*5 + sizeof(double)*2)
#define MEM_SIZE ((VAR_SIZE / 64 + 1) * 64  )
void *worker_thread(void *params_v)
{
    TWorkerParams *params = (TWorkerParams *)params_v;
    volatile char memory[MEM_SIZE];
    int *row_length  =      (int *)(memory + 0);
    int *i           =      (int *)(memory + sizeof(int)*1);
    int *j           =      (int *)(memory + sizeof(int)*2);
    int *k           =      (int *)(memory + sizeof(int)*3);
    int *rows_number =      (int *)(memory + sizeof(int)*4);
    double **a        = (double **)(memory + sizeof(int)*5);

    *row_length = params->row_length;
    *rows_number = params->rows_number;
    *a = params->a;

    for(*i = 0; *i < *row_length; ++*i) // row_length is always the same
    {
        for(*j = 0; *j < *rows_number; ++*j) // rows_number is inverse proportional
                                         // to the number of threads
        {
            for(*k = 0; *k < *row_length; ++*k) // row_length is always the same
            {
                (*a + *j * *row_length)[*k] -= 2. * *k;
            }
        }
    }
    return NULL;
}

所以我的問題是:為什么在這里使用兩個線程時,我只獲得15%的加速而不是50%? 任何幫助或建議將不勝感激。 我正在運行64位Ubuntu Linux,內核3.19.0-39通用,CPU Intel Core i5 4200M(兩個帶有多線程的物理內核),但我也在另外兩台機器上測試了它,結果相同。

編輯:如果我替換a[j*row_length + k] -= 2.; a[0] -= 2.; ,我得到預期的加速:

./threads_test1
Duration: 1823.689481 msec.
./threads_test2
Duration: 949.745232 msec.

編輯2:現在,當我用a[k] -= 2.;替換它時a[k] -= 2.; 我得到以下內容:

./threads_test1
Duration: 1039.666979 msec.
./threads_test2
Duration: 1323.460080 msec.

這個我根本無法得到。

這是一個經典問題,切換i和j for循環。

您首先遍歷列,然后在內部循環中處理行,這意味着您有更多的緩存未命中。

我的結果與原始代碼(沒有填充的第一個版本):

$ ./matrix_test1
Duration: 4620.799763 msec.
$ ./matrix_test2
Duration: 2800.486895 msec.

(實際比你的改進更好)

切換i和j的for循環后:

$ ./matrix_test1
Duration: 1450.037651 msec.
$ ./matrix_test2
Duration: 728.690853 msec.

這里加速2倍。

編輯:事實上原始並沒有那么糟糕,因為k索引仍然通過行迭代列,但是在外循環中迭代行仍然好得多。 當i上升時,你在最內循環中處理的項目越來越少,所以它仍然很重要。

EDIT2 :(刪除了塊解決方案,因為它實際上產生了不同的結果) - 但仍然應該可以利用塊來提高緩存性能。

你說的是加速的13%,但你的微積分功能所用的時間是多少,而不是其他程序。

您可以開始僅估計在沒有線程管理時間的情況下傳遞給calcul方法的時間。 您可能會在線程管理中失去重要的時間。 這可以解釋你獲得的小加速。

在其他方面,50%的加速與2線程,這是非常不可能獲得。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM