从this answer开始,GPU制造商似乎只是为特定的GPU API提供了驱动程序,并且没有诸如GPU汇编之类的东西,或者至少没有像这样的GPU汇编程序手册出版。 AMD64 programmer's manual
但是,据我了解,所有运行的进程都经过CPU,并且可以反汇编。
我的问题是:使用GPU的程序汇编是什么样的?我的假设是它将使用系统调用来操纵代表GPU的设备文件。这个假设正确吗?
答案 0 :(得分:2)
使用GPU的代码看起来像什么?
阅读有关OpenCL的更多信息(或仅对于Nvidia硬件,了解CUDA)。另请注意OpenACC!另请参阅OpenCL related resources,并阅读一些OpenCL书。阅读一些OpenCL tutorial。
实际上,您永远不会看到GPGPU的“汇编代码”。但是您将使用OpenCL进行编码(这是非常底层的,将代码调整为特定硬件非常困难且容易出错)。
AFAIK,AMD倾向于发布其大多数GPU的“机器代码规范”(例如ISA)。 Nvidia更加隐秘。请注意,SPIR是“类似汇编的”(实际上是基于LLVM bytecode的),但仍不完全是汇编程序。
我的问题是:使用GPU的程序汇编是什么样的?我的假设是它将使用系统调用来操纵代表GPU的设备文件。这个假设正确吗?
系统调用(非常特定于硬件)正在将SPIR或等效字节码(通常是特定于GPGPU的机器代码)从CPU(和虚拟内存)传输到GPU,还从GPGPU传输数据到CPU(和内存)并返回。细节非常复杂,通常是硬件制造商专有的。您更喜欢使用OpenCL(或CUDA)API和方言。 您的假设是错误的,或者至少被简化了,使其毫无意义。
也请查看osdev.org Wiki。
实际上,多个open-source数字库(例如TensorFlow,OpenCV,BLAS等)具有OpenCL后端。因此,需要几个月的时间来研究其源代码。
了解所有细节将为您提供博士学位。 Albert Cohen(以及许多其他专家)可能是您的顾问。
还阅读有关AMDGPU及其GCN的更多信息。例如,查看AMD Vega规范。
但是,据我了解,所有运行的进程都经过CPU,并且可以反汇编。
这是一个非常幼稚的声明,我相信这是错误(至少对于我喜欢编写的程序,它们都以某种方式生成运行时的代码)。而且在实践中,您将不会理解反汇编的代码(这就是decompilation如此困难的原因)。例如,生成机器代码的程序,请在Linux上查看SBCL(其REPL在每次用户交互时都发出机器代码),或查看任何meta-program,或大多数使用{ {3}}技术(实际上,大多数Java JVM都在进行JIT转换)。我的JIT-compilation Linux程序正在运行时生成C代码,将其编译为manydl.c
(即可以是shared library的plugin,然后dynamically linked)对该插件进行编译(并且可以重复所有数十万次的时间)。有关有助于生成机器代码的库示例,请参见dlopen(3)。
您还应该大致了解有关操作系统的更多信息。我强烈推荐libgccjit(可免费下载)。
答案 1 :(得分:0)
如果您使用的是Nvidia GPU,则可以查看PTX汇编代码。 PTX只是伪汇编,介于OpenCL和实际上在GPU上运行的二进制代码之间。 这是从OpenCL那里获取的方式:
Context context(device);
queue = CommandQueue(context, device); // queue to push commands for the device
Program::Sources source;
string kernel_code = opencl_code_settings(N,M)+opencl_code();
source.push_back({ kernel_code.c_str(), kernel_code.length() });
Program program(context, source);
if(program.build("-cl-fast-relaxed-math")) return false; // compile OpenCL code, return false if there is an error
const string ptx_code = program.getInfo<CL_PROGRAM_BINARIES>()[0]; // generate assembly (ptx) for OpenCL code
您正在寻找字符串ptx_code
。这是一个小的示例内核:
kernel void benchmark_1(global float* data) {
const uint n = get_global_id(0);
#pragma unroll
for(uint i=0; i<def_M; i++) data[i*def_N+n] = 0.0f;
}
此内核的PTX代码如下所示:
//
// Generated by NVIDIA NVVM Compiler
//
// Compiler Build ID: UNKNOWN
// Driver
// Based on LLVM 3.4svn
//
.version 6.2
.target sm_61, texmode_independent
.address_size 64
// .globl benchmark_1
.entry benchmark_1(
.param .u64 .ptr .global .align 4 benchmark_1_param_0
)
{
.reg .b32 %r<23>;
.reg .b64 %rd<34>;
ld.param.u64 %rd1, [benchmark_1_param_0];
mov.b32 %r1, %envreg3;
mov.u32 %r2, %ntid.x;
mov.u32 %r3, %ctaid.x;
mad.lo.s32 %r4, %r3, %r2, %r1;
mov.u32 %r5, %tid.x;
add.s32 %r6, %r4, %r5;
mul.wide.u32 %rd2, %r6, 4;
add.s64 %rd3, %rd1, %rd2;
mov.u32 %r7, 0;
st.global.u32 [%rd3], %r7;
add.s32 %r8, %r6, 15728640;
mul.wide.u32 %rd4, %r8, 4;
add.s64 %rd5, %rd1, %rd4;
st.global.u32 [%rd5], %r7;
add.s32 %r9, %r6, 31457280;
mul.wide.u32 %rd6, %r9, 4;
add.s64 %rd7, %rd1, %rd6;
st.global.u32 [%rd7], %r7;
add.s32 %r10, %r6, 47185920;
mul.wide.u32 %rd8, %r10, 4;
add.s64 %rd9, %rd1, %rd8;
st.global.u32 [%rd9], %r7;
add.s32 %r11, %r6, 62914560;
mul.wide.u32 %rd10, %r11, 4;
add.s64 %rd11, %rd1, %rd10;
st.global.u32 [%rd11], %r7;
add.s32 %r12, %r6, 78643200;
mul.wide.u32 %rd12, %r12, 4;
add.s64 %rd13, %rd1, %rd12;
st.global.u32 [%rd13], %r7;
add.s32 %r13, %r6, 94371840;
mul.wide.u32 %rd14, %r13, 4;
add.s64 %rd15, %rd1, %rd14;
st.global.u32 [%rd15], %r7;
add.s32 %r14, %r6, 110100480;
mul.wide.u32 %rd16, %r14, 4;
add.s64 %rd17, %rd1, %rd16;
st.global.u32 [%rd17], %r7;
add.s32 %r15, %r6, 125829120;
mul.wide.u32 %rd18, %r15, 4;
add.s64 %rd19, %rd1, %rd18;
st.global.u32 [%rd19], %r7;
add.s32 %r16, %r6, 141557760;
mul.wide.u32 %rd20, %r16, 4;
add.s64 %rd21, %rd1, %rd20;
st.global.u32 [%rd21], %r7;
add.s32 %r17, %r6, 157286400;
mul.wide.u32 %rd22, %r17, 4;
add.s64 %rd23, %rd1, %rd22;
st.global.u32 [%rd23], %r7;
add.s32 %r18, %r6, 173015040;
mul.wide.u32 %rd24, %r18, 4;
add.s64 %rd25, %rd1, %rd24;
st.global.u32 [%rd25], %r7;
add.s32 %r19, %r6, 188743680;
mul.wide.u32 %rd26, %r19, 4;
add.s64 %rd27, %rd1, %rd26;
st.global.u32 [%rd27], %r7;
add.s32 %r20, %r6, 204472320;
mul.wide.u32 %rd28, %r20, 4;
add.s64 %rd29, %rd1, %rd28;
st.global.u32 [%rd29], %r7;
add.s32 %r21, %r6, 220200960;
mul.wide.u32 %rd30, %r21, 4;
add.s64 %rd31, %rd1, %rd30;
st.global.u32 [%rd31], %r7;
add.s32 %r22, %r6, 235929600;
mul.wide.u32 %rd32, %r22, 4;
add.s64 %rd33, %rd1, %rd32;
st.global.u32 [%rd33], %r7;
ret;
}
例如,从PTX代码中,您可以计算FLOP和内存传输的数量,以检查通过Roofline模型运行代码的效率。