OpenMp多線程編程計時問題

在做矩陣乘法並行化測試的時候,在利用<time.h>的clock()計時時出現了一點問題。

首先看串行的程序:

// matrix_cpu.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM 2048

void matrixMul(float *A, float *B, float *C, int M, int K, int N)
{
    int i, j, k;
    for(i = 0; i < M; i++)
    {
        for(j = 0; j < N; j++)
        {
            float sum = 0.0f;
            for(k = 0; k < K; k++)
            {
                sum += A[i*k+k] * B[k*N+j];
            }
            C[i*N+j] = sum;
        }
    }
}

int main(int argc, char* argv[])
{
    float *A, *B, *C;
    clock_t start, finish;
    double duration;

    A = (float *) malloc (sizeof(float) * NUM * NUM);
    B = (float *) malloc (sizeof(float) * NUM * NUM);
    C = (float *) malloc (sizeof(float) * NUM * NUM);
    memset(A, 0, sizeof(float) * NUM * NUM);
    memset(B, 0, sizeof(float) * NUM * NUM);
    memset(C, 0, sizeof(float) * NUM * NUM);
    
    printf("Start...\n");

    start = clock();
    matrixMul(A, B, C, NUM, NUM, NUM);
    finish = clock();
    
    duration = (double)(finish - start) / CLOCKS_PER_SEC;
    printf("Time: %fs\n", duration);
    return 0;
}

在編譯後,運行該程序,得到如下結果:

[wfshen@cu05 matrix]$ ./matrix_cpu
Start...
Time: 26.130000s

由於CPU是至強E5-2650,所以算得比較快(但目前仍然是串行,也就是說單核單線程),這樣也要26秒了(在博主的i5-4200 ThinkPad上用時是171秒)。

加上time命令再運行一遍,結果如下:

[wfshen@cu05 matrix]$ time ./matrix_cpu
Start...
Time: 26.770000s

real	0m28.073s
user	0m26.779s
sys	0m0.019s

可以看到,時間與程序中統計的差不多,實際執行時間由於加了malloc等的時間所以長了一點,但還是合情合理的。

 

那麼,再來看並行的OpenMP程序:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM 2048
#define THREAD_NUM 2

void matrixMul(float *A, float *B, float *C, int M, int K, int N)
{
    int i, j, k;
#pragma omp parallel for private(j,k) num_threads(THREAD_NUM)
    for(i = 0; i < M; i++)
    {
        for(j = 0; j < N; j++)
        {
            float sum = 0.0f;
            #pragma ivdep
            for(k = 0; k < K; k++)
            {
                sum += A[i*k+k] * B[k*N+j];
            }
            C[i*N+j] = sum;
        }
    }
}

int main(int argc, char* argv[])
{
    float *A, *B, *C;
    clock_t start, finish;
    double duration;

    A = (float *) malloc (sizeof(float) * NUM * NUM);
    B = (float *) malloc (sizeof(float) * NUM * NUM);
    C = (float *) malloc (sizeof(float) * NUM * NUM);
    memset(A, 0, sizeof(float) * NUM * NUM);
    memset(B, 0, sizeof(float) * NUM * NUM);
    memset(C, 0, sizeof(float) * NUM * NUM);

    printf("Start...\n");

    start = clock();
    matrixMul(A, B, C, NUM, NUM, NUM);
    finish = clock();

    duration = (double)(finish - start) / CLOCKS_PER_SEC;
    printf("Time: %fs\n", duration);
    return 0;
}

可以看到,該OpenMP程序只使用了兩個線程,那麼運行時間理論上來說能減半。

在編譯後,運行該程序,得到如下結果:

[wfshen@cu05 matrix]$ ./matrix_omp
Start...
Time: 26.550000s

這就奇怪了,明明心裏面數了一下大概花了15秒,但是爲什麼計時還是26秒呢?

再加上time命令運行一遍:

[wfshen@cu05 matrix]$ time ./matrix_omp
Start...
Time: 26.440000s

real	0m13.438s
user	0m26.457s
sys	0m0.016s

可以看到,實際的運行時間是13秒,但是user卻超過了13秒,且幾乎是real的兩倍。

查了一下,發現了這樣的解釋:

real: 牆上時間,即程序從開啓到結束的實際運行時間
user: 執行用戶代碼所花的實際時間(不包括內核調用),指進程執行所消耗的實際CPU時間
sys:該程序在內核調用上花的時間

 在,單線程串行的時候,只有一個線程在運行,那麼user所代表的就是一個cpu的時間。然而,當到多線程的情況下,一個進程可能有多個線程並行執行,但是user把所有的線程時間都加起來了,也就是算了一個總時間,這樣,user的時間也就基本上等於單線程時的user時間。

這樣,我們把線程數調到4,再運行代碼(大概7秒):

[wfshen@cu05 matrix]$ ./matrix_omp
Start...
Time: 27.270000s
[wfshen@cu05 matrix]$ time ./matrix_omp
Start...
Time: 27.170000s

real	0m7.486s
user	0m27.176s
sys	0m0.018s

可以發現,實際運行時間7秒,CPU總時間27秒,差不多:

再把線程數調到16,再運行代碼(大概2秒多):

[wfshen@cu05 matrix]$ ./matrix_omp
Start...
Time: 33.980000s
[wfshen@cu05 matrix]$ time ./matrix_omp
Start...
Time: 33.530000s

real	0m2.241s
user	0m33.479s
sys	0m0.075s

可以發現,CPU總時間有增加的趨勢,不過實際時間還是大有減少。E5-2650是8核心16線程,再往上加線程時間反而會增長。

 

總結:在多線程的情況下,還是用time命令看時間吧。

注:轉載僅作爲筆記使用,如有侵權,請聯繫。

親測,使用clock()並行計時時,隨着合數增加,運行時間增加。建議使用gettimeofday().

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章