ich folgende OpenCL Kernel:zu globalen oder lokalen Speicher Schreiben erhöht Kernel Ausführungszeit von 10000%
kernel void ndft(
global float *re, global float *im, int num_values,
global float *spectrum_re, global float *spectrum_im,
global float *spectrum_abs,
global float *sin_array, global float *cos_array,
float sqrt_num_values_reciprocal)
{
// MATH MAGIC - DISREGARD FROM HERE -----------
float x;
float y;
float sum_re = 0;
float sum_im = 0;
size_t thread_id = get_global_id(0);
//size_t local_id = get_local_id(0);
// num_values = 24 (live environment), 48 (test)
for (int i = 0; i < num_values; i++)
{
x = cos_array[thread_id * num_values + i] * sqrt_num_values_reciprocal;
y = sin_array[thread_id * num_values + i] * sqrt_num_values_reciprocal;
sum_re = sum_re + re[i] * x + im[i] * y;
sum_im = sum_im - re[i] * y + x * im[i];
}
// MATH MAGIC DONE ----------------------------
//spectrum_re[thread_id] = sum_re;
//spectrum_im[thread_id] = sum_im;
//spectrum_abs[thread_id] = hypot(sum_re, sum_im);
float asdf = hypot(sum_re, sum_im); // this is just a dummy calculation
}
wie dies die Ausführungszeit etwa 15 us ist (Arbeitsgruppe size = 567, 14 Gruppen arbeiten , für insgesamt 7938 Fäden).
Allerdings muss ich irgendwie die Ergebnisse der Operation abrufen, wofür die letzten Zeilen stehen (auskommentiert). Sobald ich eine einzelne dieser Speicheroperationen ausführe (und es spielt keine Rolle, wenn spectrum_X
global
ist, wie im Beispiel, oder local
), erhöht sich die Ausführungszeit des Kernels auf ~ 1,4 bis 1,5 ms.
Ich dachte, dass die Erhöhung der Ausführungszeit eine Art von fixed Overhead war, so würde ich nur mehr Daten sammeln, so dass die relative Zeitverlust aufgrund dieses Effekts minimiert. Aber wenn ich meine Anzahl von Threads verdopple (d. H. Doppelt so viele Daten), verdoppelt sich auch die Ausführungszeit (auf 2,8 ~ 3,0 ms).
Ich fand heraus, dass, selbst wenn ich nur eine dieser Zeilen auskommen, habe ich die gleiche Ausführungszeit als ob ich alle drei unkommentiert. Auch wenn ich ein if (thread_id == 0)
hinzufüge und es laufe, habe ich die gleiche Ausführungszeit. Aber es ist einfach viel zu langsam (die obere Grenze für meine Anwendung ist etwa 30 US). Es läuft sogar ungefähr fünfmal schneller, wenn ich es in gewöhnlichem C-Code auf meiner CPU laufe.
Jetzt mache ich offensichtlich etwas falsch, aber ich bin mir nicht sicher, wo ich anfangen soll, nach einer Lösung zu suchen.
Als ich auf talonmies' Antwort kommentiert, ich habe auch die folgenden:
Aus dem obigen Code, ich die letzten 4 Zeilen wie
//spectrum_re[thread_id] = sum_re;
//spectrum_im[thread_id] = sum_im;
spectrum_abs[thread_id] = hypot(sum_re, sum_im);
//float asdf = hypot(sum_re, sum_im);
Wie erwartet, Ausführungszeit aussehen ~ 1,8 ms. Der erzeugte Assembler-Code für mein System ist:
//
// Generated by NVIDIA NVVM Compiler
// Compiler built on Tue Apr 03 12:42:39 2012 (1333449759)
// Driver
//
.version 3.0
.target sm_21, texmode_independent
.address_size 32
.entry ndft(
.param .u32 .ptr .global .align 4 ndft_param_0,
.param .u32 .ptr .global .align 4 ndft_param_1,
.param .u32 ndft_param_2,
.param .u32 .ptr .global .align 4 ndft_param_3,
.param .u32 .ptr .global .align 4 ndft_param_4,
.param .u32 .ptr .global .align 4 ndft_param_5,
.param .u32 .ptr .global .align 4 ndft_param_6,
.param .u32 .ptr .global .align 4 ndft_param_7,
.param .f32 ndft_param_8
)
{
.reg .f32 %f;
.reg .pred %p;
.reg .s32 %r;
ld.param.u32 %r3, [ndft_param_2];
// inline asm
mov.u32 %r18, %envreg3;
// inline asm
// inline asm
mov.u32 %r19, %ntid.x;
// inline asm
// inline asm
mov.u32 %r20, %ctaid.x;
// inline asm
// inline asm
mov.u32 %r21, %tid.x;
// inline asm
add.s32 %r22, %r21, %r18;
mad.lo.s32 %r11, %r20, %r19, %r22;
setp.gt.s32 %p1, %r3, 0;
@%p1 bra BB0_2;
mov.f32 %f46, 0f00000000;
mov.f32 %f45, %f46;
bra.uni BB0_4;
BB0_2:
ld.param.u32 %r38, [ndft_param_2];
mul.lo.s32 %r27, %r38, %r11;
shl.b32 %r28, %r27, 2;
ld.param.u32 %r40, [ndft_param_6];
add.s32 %r12, %r40, %r28;
ld.param.u32 %r41, [ndft_param_7];
add.s32 %r13, %r41, %r28;
mov.f32 %f46, 0f00000000;
mov.f32 %f45, %f46;
mov.u32 %r43, 0;
mov.u32 %r42, %r43;
BB0_3:
add.s32 %r29, %r13, %r42;
ld.global.f32 %f18, [%r29];
ld.param.f32 %f44, [ndft_param_8];
mul.f32 %f19, %f18, %f44;
add.s32 %r30, %r12, %r42;
ld.global.f32 %f20, [%r30];
mul.f32 %f21, %f20, %f44;
ld.param.u32 %r35, [ndft_param_0];
add.s32 %r31, %r35, %r42;
ld.global.f32 %f22, [%r31];
fma.rn.f32 %f23, %f22, %f19, %f46;
ld.param.u32 %r36, [ndft_param_1];
add.s32 %r32, %r36, %r42;
ld.global.f32 %f24, [%r32];
fma.rn.f32 %f46, %f24, %f21, %f23;
neg.f32 %f25, %f22;
fma.rn.f32 %f26, %f25, %f21, %f45;
fma.rn.f32 %f45, %f24, %f19, %f26;
add.s32 %r42, %r42, 4;
add.s32 %r43, %r43, 1;
ld.param.u32 %r37, [ndft_param_2];
setp.lt.s32 %p2, %r43, %r37;
@%p2 bra BB0_3;
BB0_4:
// inline asm
abs.f32 %f27, %f46;
// inline asm
// inline asm
abs.f32 %f29, %f45;
// inline asm
setp.gt.f32 %p3, %f27, %f29;
selp.f32 %f8, %f29, %f27, %p3;
selp.f32 %f32, %f27, %f29, %p3;
// inline asm
abs.f32 %f31, %f32;
// inline asm
setp.gt.f32 %p4, %f31, 0f7E800000;
mov.f32 %f47, %f32;
@%p4 bra BB0_6;
mov.f32 %f48, %f8;
bra.uni BB0_7;
BB0_6:
mov.f32 %f33, 0f3E800000;
mul.rn.f32 %f10, %f8, %f33;
mul.rn.f32 %f47, %f32, %f33;
mov.f32 %f48, %f10;
BB0_7:
mov.f32 %f13, %f48;
// inline asm
div.approx.f32 %f34, %f13, %f47;
// inline asm
mul.rn.f32 %f39, %f34, %f34;
add.f32 %f38, %f39, 0f3F800000;
// inline asm
sqrt.approx.f32 %f37, %f38; // <-- this is part of hypot()
// inline asm
mul.rn.f32 %f40, %f32, %f37;
add.f32 %f41, %f32, %f8;
setp.eq.f32 %p5, %f32, 0f00000000;
selp.f32 %f42, %f41, %f40, %p5;
setp.eq.f32 %p6, %f32, 0f7F800000;
setp.eq.f32 %p7, %f8, 0f7F800000;
or.pred %p8, %p6, %p7;
selp.f32 %f43, 0f7F800000, %f42, %p8;
shl.b32 %r33, %r11, 2;
ld.param.u32 %r39, [ndft_param_5];
add.s32 %r34, %r39, %r33;
st.global.f32 [%r34], %f43; // <-- stores the hypot's result in spectrum_abs
ret;
}
der Tat alle meine Rechenoperationen gibt es - viele fügt/mults sowie eine sqrt
für die hypot
Funktion. Aus dem obigen asm-Code, entfernte ich die zweite letzte Zeile:
st.global.f32 [%r34], %f43;
, die die Linie ist, die tatsächlich die Daten im globalen Array speichert spectrum_abs
. Dann habe ich clCreateProgramWithBinary
verwendet und die modifizierte asm-Code-Datei als Eingabe verwendet. Die Ausführungszeit sank auf 20 US-Dollar.
Mit welcher Hardware und OpenCL-Variante laufen Sie? – talonmies
@talonmies NVIDIA GeForce 555M GT, das neueste CUDA-Toolkit. – dialer
Sammeln Sie später alle Werte? Gibt es einen bestimmten Grund, warum jedes Arbeitselement 24 oder 48 aufeinanderfolgende Werte berechnen muss? Wie hast du sin_array und cos_array berechnet, bevor du sie in deinen Kernel übernommen hast? – mfa