请考虑以下CUDA的Inline PTX Assebly指南(v10.2)的摘录:
编译器假定一条
asm()
语句除更改输出操作数外没有任何副作用。为了确保在生成PTX期间不会删除或移动asm,应使用volatile关键字,例如:asm volatile ("mov.u32 %0, %%clock;" : "=r"(x));
通常,将写入的任何内存都将指定为out操作数,但是如果对用户内存有隐藏的副作用(例如,通过操作数间接访问内存位置),或者要停止任何内存优化在生成PTX期间执行的asm()语句周围,您可以在第三个冒号之后添加“内存”修饰符规范。
这听起来像两者 volatile
,:: "memory"
并且旨在指示内存中的副作用。现在,理所当然,可能会出现非内存副作用(例如trap;
)。但是-当我使用volatile
过时,也指定:: "memory"
)是没有用/没有意义的吗?
非volatile
内联的asm语句被视为其输入的纯函数:每次使用相同的显式输入运行时,都会提供相同的输出。
另外,没有任何"memory"
障碍:不读取或写入未提及为输入或输出操作数的任何内容。
听起来像volatile和::“ memory”都是用来指示内存中的副作用。
不,volatile
只是意味着输出操作数不是输入操作数的纯函数。一"memory"
撞大多是正交的,没有通过暗示volatile
您引用的示例似乎正在读取%%clock
周期计数器或每次都需要重新执行的内容,否则编译器可以执行CSE并将其吊起循环。您不想强迫编译器溢出/重新加载其在寄存器中的所有全局变量。volatile
并不意味着内存有副作用,因此这只是此用例的证明。
这仍然是一个错误的ASM模板来读取或写入编译器背后的任何其他变量(不通过明确的"m"
,"=m"
或"+m"
操作数),因为volatile
并不意味着"memory"
撞。
在GNU C内联汇编甚至"r"(pointer_variable)
也没有暗示指向的数据被读取或写入。例如,如果您对变量所做的全部工作就是将指向它的指针作为对asm
不带"memory"
麻烦的语句的输入,则可以将其优化为无效存储。我如何指示可以使用内联ASM参数*指向*的内存?
甲"memory"
撞将得到的编译器假定任何全局可到达的存储器(或通过指针输入可达)可能已被读出或写入,从而溢出/重装从围绕这样asm语句寄存器瓦尔。(除非转义分析可以证明没有其他任何东西可以指向它们,即,指向var的指针没有“转义”本地范围。就像编译器如何决定他们可以将var保留在非内联函数调用。)
那么没有"memory"
一个人是安全的volatile
吗?没有
如果"memory"
不使用闭环程序的显式输出操作数,它不会阻止asm语句进行优化。(如果没有“ = ...”操作数,则asm
语句是隐式易失的)。
如果/当执行asm模板字符串时,必须假定具有内存缓冲区的非易失性asm语句可以在抽象机上的该点修改任何可访问的内存,但是编译器仍然可以自由进行转换,从而不会发生这种情况根本没有,或者比来源要少。(例如,如果在循环中更改的其他变量都是地址未转义该函数的所有本地变量,则将其从循环中吊起。)
volatile
仍然假定非asm语句是纯函数wrt。它的显式输入和输出,因此asm("..." : "=r"(out) : "r"(in) : "memory");
如果循环在"in"
每次迭代中都使用相同的循环,则可以将其吊出循环。(只有在循环变量是asm语句无法指向的所有局部变量的情况下,这种情况才会发生(转义分析,例如非内联函数调用)。否则,"memory"
Clobber会阻止该重新排序。)
或完全优化掉(如果"out"
可以优化所有使用),而不考虑语句周围的任何内存访问。如果省略,则仅基于显式操作数进行决策volatile
。
这里没有很多用例的的"memory"
撞无volatile
; 您可以想象使用它来描述内部使用缓存来记忆结果的函数。编译器可以根据需要频繁或不频繁地运行它,而我们实际上并不关心内部缓存是否发生了突变。这是副作用,但不是有价值的副作用。
(我假设CUDA内联汇编的语义与Clang / LLVM和GCC支持/实现的GNU C内联汇编的语义相同。从引号看来,确实是这种情况。我对CUDA并不了解,所以我上面说的一切都是基于GNU C内联汇编的,因为CUDA汇编看起来是相同的,如果我写错了,请更正我,例如asm
,没有输出操作数的语句不是隐式的,volatile
或者CUDA没有指针。
由于GNU C内联asm语法是为C设计的,后来又改用于CUDA,因此可以帮助您理解设计,以C的方式进行思考,包括指针和转义分析。)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句