如何计算一个pthread矩阵乘法程序的运行时间?

3
我创建了一个矩阵乘法程序,一个串行执行,一个使用pthread并发。我需要比较它们的运行时间。我的串行代码花费约16秒来计算1000x1000的矩阵乘法,并且我用秒表检查过,结果正好如预期一样。另一方面,当我运行我的pthread矩阵乘法程序时,我会得到大约22-23秒的结果,但结果在终端上打印得比较快。我还使用秒表检查输出运行时间所需的时间,它大约为6秒,但它打印说花费了约23秒。我猜测有其他方法来检查pthread程序的运行时间。以下是我的pthread代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <assert.h>

int SIZE, NTHREADS;
int **A, **B, **C;

void init()
{
    int i, j;

    A = (int**)malloc(SIZE * sizeof(int *));
    for(i = 0; i < SIZE; i++)
        A[i] = malloc(SIZE * sizeof(int));

    B = (int**)malloc(SIZE * sizeof(int *));
    for(i = 0; i < SIZE; i++)
        B[i] = malloc(SIZE * sizeof(int));

    C = (int**)malloc(SIZE * sizeof(int *));
    for(i = 0; i < SIZE; i++)
        C[i] = malloc(SIZE * sizeof(int));

    srand(time(NULL));

    for(i = 0; i < SIZE; i++) {
        for(j = 0; j < SIZE; j++) {
            A[i][j] = rand()%100;
            B[i][j] = rand()%100;
        }
    }
}

void mm(int tid)
{
    int i, j, k;
    int start = tid * SIZE/NTHREADS;
    int end = (tid+1) * (SIZE/NTHREADS) - 1;

    for(i = start; i <= end; i++) {
        for(j = 0; j < SIZE; j++) {
            C[i][j] = 0;
            for(k = 0; k < SIZE; k++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
}

void *worker(void *arg)
{
    int tid = (int)arg;
    mm(tid);
}

int main(int argc, char* argv[])
{
    pthread_t* threads;
    int rc, i;

    if(argc != 3)
    {
        printf("Usage: %s <size_of_square_matrix> <number_of_threads>\n", argv[0]);
        exit(1);
    }

    SIZE = atoi(argv[1]);
    NTHREADS = atoi(argv[2]);
    init();
    threads = (pthread_t*)malloc(NTHREADS * sizeof(pthread_t));

    clock_t begin, end;
    double time_spent;


    begin = clock();

    for(i = 0; i < NTHREADS; i++) {
        rc = pthread_create(&threads[i], NULL, worker, (void *)i);
        assert(rc == 0);
    }

    for(i = 0; i < NTHREADS; i++) {
        rc = pthread_join(threads[i], NULL);
        assert(rc == 0);
    } 

    end = clock();

    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf("Elapsed time: %.2lf seconds.\n", time_spent);

    for(i = 0; i < SIZE; i++)
        free((void *)A[i]);
    free((void *)A);

    for(i = 0; i < SIZE; i++)
        free((void *)B[i]);
    free((void *)B);

    for(i = 0; i < SIZE; i++)
        free((void *)C[i]);
    free((void *)C);

    free(threads);

    return 0;
}
2个回答

2
这是获取已经流逝的CPU时间的方法,但不是获取已经流逝的挂钟时间的方法。要做到这一点,您需要使用time(仅具有秒级精度)或 clock_gettimeCLOCK_MONOTONIC 选项。后者更为推荐。您需要链接POSIX实时扩展(-lrt)来实现此功能。
struct timespec begin, end;
double elapsed;

clock_gettime(CLOCK_MONOTONIC, &begin);

// spawn threads to do work here

clock_gettime(CLOCK_MONOTONIC, &end);

elapsed = end.tv_sec - begin.tv_sec;
elapsed += (end.tv_nsec - begin.tv_nsec) / 1000000000.0;

在你的例子中,我猜测你使用了大约4个线程?CPU时间将是(在CPU 1中使用的时间+在CPU 2中使用的时间+在CPU 3中使用的时间+在CPU 4中使用的时间),这应该大约是绝对时间的4倍(6秒与23秒)。

我猜在你的例子中,最后一行想要输入begin和end而不是finish或start,我说得对吗?如果我保留代码不变,会出现未声明错误。如果我将它们更改为begin和end并编译,那么我会得到以下错误:mmnew.c:(.text+0x38e): undefined reference to `clock_gettime'。 - user1726549
你需要将-lrt添加到链接库列表中,以便于使用clock_gettime函数。我已经在答案中添加了这个。 - Michael Greene

1
我知道的最简单的方法是使用OpenMP。链接时加上-fopenmp。
#include <omp.h>

int main() {
    double dtime = omp_get_wtime(); //value in seconds
    //run some code
    dtime = omp_get_wtime() - dtime;

}

请注意,1000x1000矩阵乘法需要16秒是非常慢的。我的代码在i7-2600k 4.3 GHz上可以在0.03秒内完成1056x1056的计算,即使如此也只有理论最大速度的30%左右。

我在我的OpenMP矩阵乘法代码中使用了上述方法,效果非常好。另一方面,我的串行C代码在我的2.4GHz i5上大约需要16秒,我使用的是O(n^3)的朴素算法。我只是没有进行任何优化和/或使用更好的算法。 - user1726549

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接