Ich habe einen wirklich einfachen OpenCL-Code geschrieben und versucht, es auf einer Tesla K40m GPU auszuführen und die GFlops zu messen. Hier ist der Code, den ich geschrieben habe:unnötige CVT-Anweisungen in PTX-Binärdatei generiert von OpenCL
__kernel void test(__global float *GIn, __global float *GOut, int M, int N, int P) {
int X = get_global_id(0);
// Just a private variable
float temp = 1.0;
// Start of a new level of for loop
int baseIndex1 = (X) * 512;
temp += GIn[baseIndex1 + 0] * var;
temp += GIn[baseIndex1 + 1] * var;
temp += GIn[baseIndex1 + 2] * var;
temp += GIn[baseIndex1 + 3] * var;
temp += GIn[baseIndex1 + 4] * var;
temp += GIn[baseIndex1 + 5] * var;
temp += GIn[baseIndex1 + 6] * var;
temp += GIn[baseIndex1 + 7] * var;
temp += GIn[baseIndex1 + 8] * var;
temp += GIn[baseIndex1 + 9] * var;
temp += GIn[baseIndex1 + 10] * var;
...
temp += GIn[baseIndex1 + 510] * var;
temp += GIn[baseIndex1 + 511] * var;
GOut[baseIndex1] = temp;
}
ich bereitgestellt habe diesen Kernel auf meiner GPU mit global_work_size von [1048576] und local_work_size von [128]. Die Gesamtzahl der Gleitkommaoperationen, die es pro Sekunde ausführen kann, liegt bei 1,6 GFlops, was extrem niedrig ist. Ich nehme an, ich mache nur einzelne Operationen und auch Speicher wird sequentiell gelesen. Ich habe beschlossen, bei der generierten PTX Code einen Blick zu nehmen:
.version 5.0
.target sm_35, texmode_independent
.address_size 64
// .globl test
.func (.param .b64 func_retval0) get_global_id
(
.param .b32 get_global_id_param_0
)
;
.entry test(
.param .u64 .ptr .global .align 4 test_param_0,
.param .u64 .ptr .global .align 4 test_param_1,
.param .u32 test_param_2,
.param .u32 test_param_3,
.param .u32 test_param_4
)
{
.reg .f32 %f<1537>;
.reg .b32 %r<515>;
.reg .b64 %rd<1543>;
ld.param.u64 %rd1, [test_param_0];
ld.param.u64 %rd2, [test_param_1];
mov.u32 %r1, 0;
// Callseq Start 0
{
.reg .b32 temp_param_reg;
// <end>}
.param .b32 param0;
st.param.b32 [param0+0], %r1;
.param .b64 retval0;
call.uni (retval0),
get_global_id,
(
param0
);
ld.param.b64 %rd3, [retval0+0];
//{
}// Callseq End 0
cvt.u32.u64 %r2, %rd3;
mul.lo.s32 %r3, %r2, 512;
cvt.s64.s32 %rd4, %r3;
shl.b64 %rd5, %rd4, 2;
add.s64 %rd6, %rd1, %rd5;
ld.global.f32 %f1, [%rd6];
mul.f32 %f2, %f1, 0f3FC00000;
add.f32 %f3, %f2, 0f3F800000;
add.s32 %r4, %r3, 1;
cvt.s64.s32 %rd7, %r4;
shl.b64 %rd8, %rd7, 2;
add.s64 %rd9, %rd1, %rd8;
ld.global.f32 %f4, [%rd9];
mul.f32 %f5, %f4, 0f3FC00000;
add.f32 %f6, %f3, %f5;
add.s32 %r5, %r3, 2;
cvt.s64.s32 %rd10, %r5;
shl.b64 %rd11, %rd10, 2;
add.s64 %rd12, %rd1, %rd11;
ld.global.f32 %f7, [%rd12];
mul.f32 %f8, %f7, 0f3FC00000;
add.f32 %f9, %f6, %f8;
add.s32 %r6, %r3, 3;
cvt.s64.s32 %rd13, %r6;
shl.b64 %rd14, %rd13, 2;
add.s64 %rd15, %rd1, %rd14;
ld.global.f32 %f10, [%rd15];
mul.f32 %f11, %f10, 0f3FC00000;
add.f32 %f12, %f9, %f11;
add.s32 %r7, %r3, 4;
cvt.s64.s32 %rd16, %r7;
shl.b64 %rd17, %rd16, 2;
add.s64 %rd18, %rd1, %rd17;
ld.global.f32 %f13, [%rd18];
mul.f32 %f14, %f13, 0f3FC00000;
add.f32 %f15, %f12, %f14;
add.s32 %r8, %r3, 5;
cvt.s64.s32 %rd19, %r8;
shl.b64 %rd20, %rd19, 2;
add.s64 %rd21, %rd1, %rd20;
ld.global.f32 %f16, [%rd21];
mul.f32 %f17, %f16, 0f3FC00000;
add.f32 %f18, %f15, %f17;
Wie es in dem Code klar ist, ich habe unnötigen cvt und shl Anweisungen, die ich sind eine mögliche Ursache von Kopfnehmen.
Jetzt habe ich zwei Fragen hier: (1) Wie soll ich meinen Kernel umschreiben, um zwei erwähnte Anweisungen loszuwerden und den Kernel schneller zu machen? (2) Gibt es in meinem Code eine andere Quelle für Overhead, von der ich nichts weiß?
Sie sind speichergebunden, so dass FLOPS leiden werden. Sie müssen globale Speicherzugriffe zusammenführen, um die volle Speicherbandbreite zu erhalten. – Dithermaster