2016-11-11 5 views
0

Я пытаюсь вычислить максимальную пропускную способность, возможную для моего GPU для матричной матрицы *, умножения плотности, используя как можно больше вычислительных ресурсов.cuSparse Пропускная способность снижается при увеличении числа итераций

Для того, чтобы достичь этого я попробовал два метода:

  1. Выделяет память для й и А на хосте. Храните x и A на хосте. Выделите память для x и A на устройстве. Храните x и A на устройстве. Таймер запуска. Выполняйте разреженную матрицу * плотное векторное умножение через cusparsecsrmv в цикле и запускайте cusparsecsrmv за NUM_ITERATIONS раз. Таймер остановки. Скопируйте y из устройства в хост и проверьте результат для точности.

  2. Выделите память для x и A на хосте. Храните x и A на хосте. Выделите память для массива x и A на устройстве (т. Е. X [NUM_IMPS], A [NUM_IMPS]). Храните X и A на устройстве. Таймер запуска. Выполняйте разреженное умножение матрицы * с помощью cusparsecsrmv в цикле и запускайте cusparsecsrmv за NUM_IMPS раз на каждом A [i] * x [i]. Таймер остановки. Скопируйте y [NUM_IMPS-1] с устройства на хост и проверьте результат для точности.

Вот мой код для метода 1:

// == Start timer for just measuring multiplication == 
QueryPerformanceFrequency(&Frequency1); 
QueryPerformanceCounter(&StartingTime1); 


// Sparse matrix * dense vector multiplication 
/* exercise Level 2 routines (csrmv) */ 
for (int i = 0; i < NUM_ITERATIONS; i++) { 
status = cusparseScsrmv(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, m, n, nnz, 
&alpha, descr, cooVal, csrRowPtr, cooColIndex, 
&xVal[0], &beta, &y[0]); 
} 

// == End time for just measuring multiplication == 
QueryPerformanceCounter(&EndingTime1); 
ElapsedMicroseconds1.QuadPart = EndingTime1.QuadPart - StartingTime1.QuadPart; 
ElapsedMicroseconds1.QuadPart *= 1000000; 
ElapsedMicroseconds1.QuadPart /= Frequency1.QuadPart; 

Вот мой код для метода 2:

// == Start timer for just measuring multiplication == 
QueryPerformanceFrequency(&Frequency1); 
QueryPerformanceCounter(&StartingTime1); 

for (int i = 0; i < NUM_IMPS; i++) { 
status = cusparseScsrmv(handle_array[i], CUSPARSE_OPERATION_NON_TRANSPOSE, m, n, nnz, 
    &alpha, descr_array[i], cooVal_array[i], csrRowPtr_array[i], cooColIndex_array[i], 
    &xVal_array[i][0], &beta, &y_array[i][0]); 
} 

// == End time for just measuring multiplication == 
QueryPerformanceCounter(&EndingTime1); 
ElapsedMicroseconds1.QuadPart = EndingTime1.QuadPart - StartingTime1.QuadPart; 
ElapsedMicroseconds1.QuadPart *= 1000000; 
ElapsedMicroseconds1.QuadPart /= Frequency1.QuadPart; 

Если NUM_ITERATIONS или NUM_IMPS = 1, они получают такую ​​же пропускную способность. Если NUM_IMPS = 10, максимальная пропускная способность. Однако, как только NUM_IMPS = 100 или более, пропускная способность начинает уменьшаться. Аналогично с NUM_ITERATIONS он начинает расти, но как только я поставил NUM_ITERATIONS на супер большом количестве, скажем, 100000 пропускная способность упадет ниже пропускной способности для NUM_ITERATIONS = 1.

Почему это происходит? Я бы ожидал, что thorughput просто насытится в какой-то момент и не сможет подняться выше, , но не уменьшится.

Мои мысли связаны с чем-то с несколькими вызовами cusparsecsrmv, которые GPU боятся, или, возможно, графическому процессору необходимо охладиться, поэтому он замедляется, поэтому пропускная способность снижается, но все это не кажется разумным выводы для меня.

ответ

2

Цитируя documentation:

Библиотечные функции cuSPARSE выполняются асинхронно с по отношению к хозяину и может вернуть управление приложением на хосте, прежде чем результат готов. Разработчики могут использовать функцию cudaDeviceSynchronize() , чтобы гарантировать выполнение конкретной процедуры библиотеки cuSPARSE .

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

+0

Каков правильный способ измерения времени? Добавьте команду cudaDeviceSynchronize() сразу после всех вызовов? Я сделал это, и производительность танковала, намного ниже, чем я ожидал. – Veridian

+2

Исполнение не было «танком». Вы только начали правильно его измерять. – talonmies

+0

, так сколько итераций, по-вашему, я должен запустить до запуска cudaDeviceSynchronize? Кроме того, я просто запускаю cudaDeviceSynchronize без аргументов? – Veridian

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