2013-04-03 2 views
2

Я пытаюсь оценить производительность простых операций с матричной матрицей GPU с помощью ArrayFire.Сроки в ArrayFire

В частности, с учетом

int N1 = something; 
int N2 = something; 

array A_D = constant(1.,N1*N2,1,f64); 
array B_D = constant(1.,N1*N2,1,f64); 
array C_D = constant(1.,N1*N2,1,f64); 
array D_D = constant(1.,N1*N2,1,f64); 

Я хотел бы выполнить синхронизацию следующей инструкции

D_D = A_D + B_D + C_D + 3.; 

Я использую два подхода. Первый

timer time_last; 
time_last = timer::start(); 

D_D = A_D + B_D + C_D + 3.; 

double elapsed = timer::stop(time_last); 
printf("elapsed time using start and stop = %g ms \n",1000.*elapsed); 

второй один является определяющим следующую функцию

void timing_test() 
{ 
    int N1 = something; 
int N2 = something; 

    array A_D = constant(1.,N1*N2,1,f64); 
    array B_D = constant(1.,N1*N2,1,f64); 
    array C_D = constant(1.,N1*N2,1,f64); 
    array D_D = constant(1.,N1*N2,1,f64); 

    D_D = A_D + B_D + C_D + 3.; 
} 

и затем вызвать

printf("elapsed time using timeit %g ms \n", 1000.*timeit(timing_test)); 

я получил следующие результаты:

(N1,N2)=(256,256) первый подход = 0.0456ms второй подход = 0.264ms

(N1,N2)=(512,512) Первый подход = 0.0451ms Второй подход = 0.264ms

(N1,N2)=(1024,1024) Первый подход = 0.0457ms Второй подход = 0.263ms

(N1,N2)=(2048,2048) Первый подход = 0.127ms Второй подход = 0.265ms

Я также с использованием после «ручной кодированной» версии выражения в соответствии с

cudaEventCreate(&start); 
cudaEventCreate(&stop); 
cudaEventRecord(start, 0); 

eval_matrix_wrap_handcoded(A_D,B_D,C_D,D_D,N1*N2); 

cudaEventRecord(stop, 0); 
cudaEventSynchronize(stop); 
cudaEventElapsedTime(&time, start, stop); 

template <class T1, class T2, class T3, class T4> 
__global__ inline void evaluation_matrix_handcoded(T1 *A_D, T2 *B_D, T3 *C_D, T4 *D_D, int NumElements) 
{ 
    const int i = blockDim.x * blockIdx.x + threadIdx.x; 
    if(i < NumElements) D_D[i]=A_D[i]+B_D[i]+C_D[i]+3.; 
} 

__host__ void eval_matrix_wrap_handcoded(double *A_D, double *B_D, double *C_D, double *D_D, int NumElements) 
{ 
    dim3 dimGrid(iDivUp(NumElements,dimBlock.x)); 
    evaluation_matrix_handcoded<<<dimGrid,dimBlock>>>(A_D,B_D,C_D,D_D,NumElements); 
} 

получения следующего

(N1,N2)=(256,256)0.0897ms

(N1,N2)=(512,512)0.339ms

(N1,N2)=(1024,1024)1.3ms

(N1,N2)=(2048,2048)5.37ms

Странная вещь:

  1. Результаты двух подходов разные. Это может быть связано с накладными расходами на функции, но в любом случае странно, что эти накладные расходы меняются, когда (N1,N2)=(2048,2048).
  2. Результаты двух подходов практически не зависят от размеров матрицы.
  3. Результаты значительно отличаются от «выраженной вручную» версии выражения (я предполагаю, что библиотека должна иметь компромисс производительности производительности).

Обратите внимание, что перед любой операцией, я прогрев GPU с использованием кода

array test1(1,5); 
test1(0,0)=1; 
test1(0,1)=2; 
test1(0,2)=3; 
test1(0,3)=4; 
test1(0,4)=5; 

Может кто-то помочь мне интерпретировать полученные результаты? Благодарю.

РЕДАКТИРОВАТЬ ответить на следующий PAVAN'S

Первого метод модифицировано для

timer time_last; 
time_last = timer::start(); 

D_D = A_D + B_D + C_D + 3.; 
D_D.eval(); 
af::sync(); 

double elapsed = timer::stop(time_last); 
printf("elapsed time using start and stop = %g ms \n",1000.*elapsed); 

Второго метода модифицированного

void timing_test() 
{ 
    int N1 = something; 
    int N2 = something; 

    array A_D = constant(1.,N1*N2,1,f64); 
    array B_D = constant(1.,N1*N2,1,f64); 
    array C_D = constant(1.,N1*N2,1,f64); 
    array D_D = constant(1.,N1*N2,1,f64); 

    D_D = A_D + B_D + C_D + 3.; 
    D_D.eval(); 
} 

Однако время сейчас

`(N1,N2)=(256,256)` first approach = `14.7ms` second approach = `2.04ms` 

`(N1,N2)=(512,512)` first approach = `14.3ms` second approach = `2.04ms` 

`(N1,N2)=(1024,1024)` first approach = `14.09ms` second approach = `2.04ms` 

`(N1,N2)=(2048,2048)` first approach = `16.47ms` second approach = `2.04ms` 

, и у меня все еще есть разные тайминги и независимы от размера векторов.

Если я изменить первый метод

D_D = A_D + B_D + C_D + 3.; 
D_D.eval(); 

timer time_last; 
time_last = timer::start(); 

D_D = A_D + B_D + C_D + 3.; 
D_D.eval(); 
af::sync(); 

double elapsed = timer::stop(time_last); 
printf("elapsed time using start and stop = %g ms \n",1000.*elapsed); 

а именно, я «увеличение» ГПУ разогрев этап, я получить, для первого метода,

`(N1,N2)=(256,256)` `0.19ms` 

`(N1,N2)=(512,512)` `0.42ms` 

`(N1,N2)=(1024,1024)` `1.18ms` 

`(N1,N2)=(2048,2048)` `4.2ms` 

, который появляется более разумный для меня, поскольку время зависит от размера данных и ближе к ручному кодированию.

ВТОРОЙ EDIT Подводя итог: я приходилась на ответ и комментарий, и, на первый подход, я использую

D_D = A_D + B_D + C_D + 3.; 
D_D.eval(); 

timer time_last; 
af::sync(); 
time_last = timer::start(); 

D_D = A_D + B_D + C_D + 3.; 
D_D.eval(); 
af::sync(); 

double elapsed = timer::stop(time_last); 
printf("elapsed time using start and stop = %g ms \n",1000.*elapsed); 

я получить следующие (новые) результаты:

`(N1,N2)=(256,256)` `0.18ms` 

`(N1,N2)=(512,512)` `0.30ms` 

`(N1,N2)=(1024,1024)` `0.66ms` 

`(N1,N2)=(2048,2048)` `2.18ms` 

ответ

1

ArrayFire использует всякий раз компилятор для мудрых операций элементов (это включает в себя арифметические, логические, trignometric и другие математические операции).

Это означает что-то вроде

D_D = A_D + B_D + C_D + 3.; 

хранится в виде выражения, пока значение d_d не запрашивается пользователем или другой функции, не JIT.

Вы можете принудительно оценить эти выражения, если используете функцию af::eval() или метод eval().

Для вашей конкретной проблемы, пожалуйста, используйте D_D.eval() для обоих методов. Вам нужно будет сделать af::sync() также для первого метода. timeit() не нужно явно синхронизировать.

+0

Большое спасибо за ваш ответ. Я применил ваши предложения, но, похоже, у меня все еще есть проблемы. Я также отредактировал свой пост, чтобы представить последние результаты. Похоже, что если я «увеличу количество» разминки ГПУ, я получу более разумные результаты. Являются ли они окончательными таймингами. Я что-то пропустил? – JackOLantern

+0

Спасибо. Я обновил свой пост с новыми результатами, включая дополнительный 'af :: sync()', который вы рекомендовали. Они выглядят разумно, так как время увеличивается с размером векторов. Но должен ли я теперь делать какие-либо действия для случая «timeit» (второй подход) для получения аналогичных результатов? Я все еще получаю постоянное время '2ms'. Заранее благодарим за любую дальнейшую помощь. – JackOLantern

Смежные вопросы