我有这个旧代码转换球面到笛卡尔三维坐标:
TDVector3D Cartesian3D_asm(const double &Theta, const double &Phi)
{
TDVector3D V;
__asm__
{
mov eax,[ebp+0x0C]
mov edx,[ebp+0x10]
fld qword ptr [eax] // ST0=T Theta
fsincos // ST1=sin(T) ST0=cos(T)
fxch ST(1) // ST1=cos(T) ST0=sin(T)
fld qword ptr [edx] // ST2=cos(T) ST1=sin(T) ST0=P Phi
fsincos // ST3=cos(T) ST2=sin(T) ST1=sin(P) ST0=cos(P)
fmul ST,ST(2) // ST3=cos(T) ST2=sin(T) ST1=sin(P) ST0=cos(P)*sin(T)
fstp qword ptr V.X // ST2=cos(T) ST1=sin(T) ST0=sin(P)
fmulp ST(1),ST // ST1=cos(T) ST0=sin(P)*sin(T)
fstp qword ptr V.Y // ST0=cos(T)
fstp qword ptr V.Z // Coprocesseur vide
fwait
}
return V;
}
使用此TDVector3D结构:
typedef struct TDVector3D {
double X, Y, Z;
TDVector3D(double x, double y, double z): X(x), Y(y), Z(z) { }
} TDVector3D;
非汇编代码为:
TDVector3D Cartesian3D(const double &Theta, const double &Phi)
{
double X, Y, Z;
X = Y = sin(Theta);
X *= cos(Phi);
Y *= sin(Phi);
Z = cos(Theta);
return TDVector3D(X, Y, Z);
}
我找到了SinCos的样本:
void SinCos(double Theta, double *sinT, double *cosT)
{
__asm__ ("fsincos" : "=t" (*cosT), "=u" (*sinT) : "0" (Theta));
}
我试图转换我的旧代码,但我完全失去了与“u”,“0”,“t”(谁是谁关于ST0,ST1,...)。
1条答案
按热度按时间nzk0hqpo1#
https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html-“=t”表示输出选取栈顶寄存器st(0)。“0”选取与操作数0相同的位置,这是“匹配约束”,因此st(0)也是如此,这是有意义的,因为这是fsincos的输入。“=u”是另一个输出st(1),这并不奇怪。
IDK为什么要使用内联asm,而不是让编译器从
<math.h>
/<cmath>
优化sincos()
(GNU扩展);数学库函数调用是好的,并且可能比x87指令快。如果在循环中调用Cartesian3D
,甚至可能自动矢量化,产生2或4个结果,工作量与一个结果大致相同。还有,为什么要用double by const引用呢?它足够小,可以通过值传递。顺便说一句,在现代代码中使用x87的主要原因是为了80位扩展精度。如果你需要的话,仍然只需要使用sincosl。GCC可能会将它内联到
fsincos
指令中,或者可能会调用一个库函数,这可能会更快;https://agner.org/optimize乘以fsincos,微指令数为60-120,周期延迟为60-140(奇怪的是,没有报告吞吐量)。此外,如果您坚持使用asm来强制它运行x87 fsincos,则不需要将其转换为一位asm()语句,您可以称之为工作SinCos()两次,对于两个独立的输入,编译器将处理加载/存储和fxchg。嗯,我认为您不需要,但GCC和clang在64位模式下做得相当差,希望将数据弹回XMM寄存器以进行乘法运算。https://godbolt.org/z/1j9df4cro,无论是按值传递(如果不是内联,则强制它首先将XMM寄存器溢出到内存)还是按引用传递。
即使在32位模式下,我想它也会自动向量化乘法,希望使用
mulpd
。https://godbolt.org/z/sTd43zono显示GCC-m32 -O3 -mfpmath=387 -fno-tree-vectorize
使用 Package 函数生成高效的asm,指令数与内联asm{}
块大致相同。与MSVC低效的
asm{}
块不同,GNU C inline asm可以高效地 Package 一条指令,并告诉编译器在哪些寄存器中放置输入和查找输出。没有开销,因此只要编译器可以在内联的asm("":::)
语句之间生成高效的代码,使用一个更复杂的asm
语句就没有好处。对于没有自动矢量化的32位模式是这样的,但对于64位模式则不是这样(除非您也使用
-mfpmath=387
作为编译单元!Godbolt)