Как измерить общую производительность параллельных программ (с папи)
Я спросил себя, как лучше всего измерить производительность (в флопах) параллельной программы. Я читал о papi_flops. Кажется, это работает нормально для последовательной программы. Но я не знаю, как я могу измерить общую производительность параллельной программы.
Я хотел бы измерить производительность функции blas / lapack, в моем примере ниже gemm. Но я также хочу измерить другую функцию, особенно функции, где номер операции не известен. (В случае с gemm операции известны (ops(gemm) = 2*n^3), поэтому я могу рассчитать производительность как функцию от количества операций и времени выполнения.) Библиотека (я использую Intel MKL) порождает темы автоматически. Поэтому я не могу измерить производительность каждого потока в отдельности, а затем снизить ее.
Это мой пример:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mkl.h"
#include "omp.h"
#include "papi.h"
int main(int argc, char *argv[] )
{
int i, j, l, k, n, m, idx, iter;
int mat, mat_min, mat_max;
int threads;
double *A, *B, *C;
double alpha =1.0, beta=0.0;
float rtime1, rtime2, ptime1, ptime2, mflops;
long long flpops;
#pragma omp parallel
{
#pragma omp master
threads = omp_get_num_threads();
}
if(argc < 4){
printf("pass me 3 arguments!\n");
return( -1 );
}
else
{
mat_min = atoi(argv[1]);
mat_max = atoi(argv[2]);
iter = atoi(argv[3]);
}
m = mat_max; n = mat_max; k = mat_max;
printf (" Initializing data for matrix multiplication C=A*B for matrix \n"
" A(%ix%i) and matrix B(%ix%i)\n\n", m, k, k, n);
A = (double *) malloc( m*k * sizeof(double) );
B = (double *) malloc( k*n * sizeof(double) );
C = (double *) malloc( m*n * sizeof(double) );
printf (" Intializing matrix data \n\n");
for (i = 0; i < (m*k); i++)
A[i] = (double)(i+1);
for (i = 0; i < (k*n); i++)
B[i] = (double)(-i-1);
memset(C,0,m*n*sizeof(double));
// actual meassurment
for(mat=mat_min;mat<=mat_max;mat+=5)
{
m = mat; n = mat; k = mat;
for( idx=-1; idx<iter; idx++ ){
PAPI_flops( &rtime1, &ptime1, &flpops, &mflops );
cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans,
m, n, k, alpha, A, k, B, n, beta, C, n);
PAPI_flops( &rtime2, &ptime2, &flpops, &mflops );
}
printf("%d threads: %d in %f sec, %f MFLOPS\n",threads,mat,rtime2-rtime1,mflops);fflush(stdout);
}
printf("Done\n");fflush(stdout);
free(A);
free(B);
free(C);
return 0;
}
Это один выход (для размера матрицы 200):
1 threads: 200 in 0.001459 sec, 5570.258789 MFLOPS
2 threads: 200 in 0.000785 sec, 5254.993652 MFLOPS
4 threads: 200 in 0.000423 sec, 4919.640137 MFLOPS
8 threads: 200 in 0.000264 sec, 3894.036865 MFLOPS
По времени выполнения мы видим, что функция gemm масштабируется. Но флопы, которые я измеряю, это только производительность нити 0.
Мой вопрос: как я могу измерить общую производительность? Я благодарен за любой вклад.
1 ответ
Во-первых, мне просто любопытно - зачем вам шлепки? тебе не важно, сколько времени уходит? или, может быть, по сравнению с другими библиотеками BLAS?
PAPI - это основанная на потоках небольшая помощь сама по себе.
То, что я хотел бы сделать, это измерить вызов функции и посмотреть, как меняется время с количеством порождаемых им потоков. Он не должен порождать больше потоков, чем физических ядер (здесь HT не годится). Затем, если матрица достаточно большая, а машина не загружена, время следует просто разделить на количество потоков. Например, 10 секунд на 4 ядра должны стать 2,5 секунды.
Кроме этого, есть две вещи, которые вы можете сделать, чтобы действительно измерить это:
1. Используйте то, что вы используете сейчас, но введите код начала / конца измерения вокруг кода BLAS. Один из способов сделать это (в linux) - предварительно загрузить библиотеку, которая определяет pthread_start, и использовать ваши собственные функции, которые вызывают оригиналы, но выполняют некоторые дополнительные измерения. Еще один способ переопределить указатель на функцию, когда процесс уже запущен (= батут). В Linux это в GOT/PLT, а в Windows все сложнее - ищите библиотеку.
2. Используйте oprofile или какой-либо другой профилировщик, чтобы сообщить о количестве инструкций, выполненных за то время, за которое вы заботитесь. Или, еще лучше, сообщить о количестве выполненных инструкций с плавающей запятой. Небольшая проблема с этим заключается в том, что инструкции SSE умножаются или добавляют 2 или более двойных за раз, так что вам придется учитывать это. Я думаю, вы можете предположить, что они всегда используют максимально возможные операнды.