Я пытаюсь узнать, как сделать GPU оптимальным ядерным ядрам OpenCL, я взял пример умножения матрицы, используя квадратные плитки в локальной памяти. Однако Я получил в лучшем случае только ~ 10-кратное ускорение (~ 50 Gflops) по сравнению с numpy.dot() (5 Gflops, он использует BLAS).Умножение матрицы OpenCL должно быть быстрее?
Я нашел исследования, где получили ускорение> 200x (> 1000 Gflops). ftp://ftp.u-aizu.ac.jp/u-aizu/doc/Tech-Report/2012/2012-002.pdf Я не знаю, что я делаю неправильно, или просто из-за моего GPU (nvidia GTX 275). Или это связано с некоторыми издержками pyOpenCl. Но я также оценил, сколько времени требуется для копирования результата с GPU в RAM, и это всего лишь ~ 10% от времени умножения матрицы.
#define BLOCK_SIZE 22
__kernel void matrixMul(
__global float* Cij,
__global float* Aik,
__global float* Bkj,
__const int ni,
__const int nj,
__const int nk
){
// WARRNING : interchange of i and j dimension lower the performance >2x on my nV GT275 GPU
int gj = get_global_id(0); int gi = get_global_id(1);
int bj = get_group_id(0); int bi = get_group_id(1); // Block index
int tj = get_local_id(0); int ti = get_local_id(1); // Thread index
int oj = bi*BLOCK_SIZE; int oi = bj*BLOCK_SIZE;
float Csub =0;
__local float As [BLOCK_SIZE][BLOCK_SIZE];
__local float Bs [BLOCK_SIZE][BLOCK_SIZE];
for (int ok = 0; ok < nk; ok += BLOCK_SIZE) {
As[ti][tj] = Aik[ nk*(gi ) + tj + ok ]; // A[i][k]
Bs[ti][tj] = Bkj[ nj*(ti+ok) + gj ]; // B[k][j]
barrier(CLK_LOCAL_MEM_FENCE);
for (int k = 0; k < BLOCK_SIZE; ++k) Csub += As[ti][k] * Bs[k][tj];
barrier(CLK_LOCAL_MEM_FENCE);
}
Cij[ nj * (gi) + gj ] = Csub;
}
Примечание - странный BLOCK_SIZE = 22 является максимальным BLOCK_SIZE, который делает подходит к максимальному work_group_size, который 512 на моем GPU. В этом коде должно быть указано условие BLOCK_SIZE^2 < max work_group_size. 22 = INT (SQRT (512)). Я попробовал также BLOCK_SIZE = 16 или 8, но был медленнее tan с 22.
Я также пробовал простую матрицуMul (без использования локальной памяти), но это было даже в 10 раз медленнее, чем numpy.dot(). Я скопировал здесь код http://gpgpu-computing4.blogspot.cz/2009/10/matrix-multiplication-3-opencl.html говорят, что даже простая версия (без локальной памяти) должна работать на 200 раз быстрее, чем у процессора? Я этого не делаю.
dependecne производительности в моем случае:
N = 220 numpy 3.680 [Gflops] GPU 16.428 [Gflops] speedUp 4.464
N = 330 numpy 4.752 [Gflops] GPU 29.487 [Gflops] speedUp 6.205
N = 440 numpy 4.914 [Gflops] GPU 37.096 [Gflops] speedUp 7.548
N = 550 numpy 3.849 [Gflops] GPU 47.019 [Gflops] speedUp 12.217
N = 660 numpy 5.251 [Gflops] GPU 49.999 [Gflops] speedUp 9.522
N = 770 numpy 4.565 [Gflops] GPU 48.567 [Gflops] speedUp 10.638
N = 880 numpy 5.452 [Gflops] GPU 44.444 [Gflops] speedUp 8.152
N = 990 numpy 4.976 [Gflops] GPU 42.187 [Gflops] speedUp 8.478
N = 1100 numpy 5.324 [Gflops] GPU 83.187 [Gflops] speedUp 15.625
N = 1210 numpy 5.401 [Gflops] GPU 57.147 [Gflops] speedUp 10.581
N = 1320 numpy 5.450 [Gflops] GPU 48.936 [Gflops] speedUp 8.979
Примечание - номер «Gflops» получается как N^3/время, и это не включает в себя время, необходимое для копирования результатов от GPU к оперативной памяти, но на этот раз всего лишь несколько процентов от общего времени, особенно для N> 1000
может быть более изобразительным время в secons:
N = 220 numpy 0.003 [s] GPU 0.001 [s] load 0.001 [s] speedUp 5.000
N = 330 numpy 0.008 [s] GPU 0.001 [s] load 0.001 [s] speedUp 7.683
N = 440 numpy 0.017 [s] GPU 0.002 [s] load 0.001 [s] speedUp 7.565
N = 550 numpy 0.043 [s] GPU 0.004 [s] load 0.001 [s] speedUp 11.957
N = 660 numpy 0.055 [s] GPU 0.006 [s] load 0.002 [s] speedUp 9.298
N = 770 numpy 0.100 [s] GPU 0.009 [s] load 0.003 [s] speedUp 10.638
N = 880 numpy 0.125 [s] GPU 0.010 [s] load 0.000 [s] speedUp 12.097
N = 990 numpy 0.195 [s] GPU 0.015 [s] load 0.000 [s] speedUp 12.581
N = 1100 numpy 0.250 [s] GPU 0.031 [s] load 0.000 [s] speedUp 8.065
N = 1210 numpy 0.328 [s] GPU 0.031 [s] load 0.000 [s] speedUp 10.581
N = 1320 numpy 0.422 [s] GPU 0.047 [s] load 0.000 [s] speedUp 8.979
Я думал, что, возможно, некоторые скорости импровизации ement можно получить, используя async_work_group_copy или даже read_imageui для копирования блоков в локальную память. Но я не понимаю, почему у меня такая большая разница, когда я использую в основном тот же код, что и люди, которые говорят, что у них 200-кратное ускорение ?????
Привет, спасибо, но мой вопрос в том, чтобы о профессиональном кровоточении кромки производительности sgemm/dgemm. Я просто хочу узнать, как сделать вполне эффективные ядра OpenCL ** вообще **, и умножение матрицы, которое я выбрал, просто потому, что это довольно распространенный пример узкого места глобальной памяти. Поэтому я бы хотел, чтобы какой-то общий ответ, что я делаю неправильно, когда он в несколько раз медленнее, чем должен быть. У меня нет верхнего процессора (у меня Core 2 Quad Q9450 на 2,66 ГГц), а не на верхнем графическом процессоре и не оптимизированной установке MKL/LAPACK (только по умолчанию в Python (x, y)). Но это не вопрос моего вопроса. –
Я понимаю. Но я хотел сначала исправить ваши сравнения. Бумага, на которую вы ссылаетесь, истекает кровью, когда вы ее определяете, и вы сравниваете ее с чем-то, что далеко от нее. Но в любом случае я обнаружил ошибку в вашем расчете Gflops/s. Он должен быть 2.0 * n^3. –
Согласно википедии и другим сайтам, GTX 275 способен принимать около 1000 GFlops/s (я предполагаю SP). Вы получаете около 100 GFlops/s. Это 10% эффективности. Я не знаю, где ваша неэффективность. Я оптимизировал на CPU пока не GPU. –