Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 为什么SSE标量sqrt(x)比rsqrt(x)*x慢?_Performance_Assembly_Floating Point_X86_Sse - Fatal编程技术网

Performance 为什么SSE标量sqrt(x)比rsqrt(x)*x慢?

Performance 为什么SSE标量sqrt(x)比rsqrt(x)*x慢?,performance,assembly,floating-point,x86,sse,Performance,Assembly,Floating Point,X86,Sse,我在一个Intel core Duo上分析了我们的一些核心数学,在研究各种平方根方法时,我注意到了一些奇怪的事情:使用SSE标量运算,求倒数平方根并将其相乘以获得sqrt比使用本机sqrt操作码更快 我正在用一个类似以下的循环来测试它: inline float TestSqrtFunction(float-in); void TestFunc() { #定义数组化4096 #定义NUMITERS 16386 float flIn[ARRAYSIZE];//用随机数填充(0..2^22) flo

我在一个Intel core Duo上分析了我们的一些核心数学,在研究各种平方根方法时,我注意到了一些奇怪的事情:使用SSE标量运算,求倒数平方根并将其相乘以获得sqrt比使用本机sqrt操作码更快

我正在用一个类似以下的循环来测试它:

inline float TestSqrtFunction(float-in);
void TestFunc()
{
#定义数组化4096
#定义NUMITERS 16386
float flIn[ARRAYSIZE];//用随机数填充(0..2^22)
float flOut[ARRAYSIZE];//填充0以强制提取到一级缓存
cyclecounter.Start();
对于(int i=0;i
我已经用几个不同的主体为TestSqrtFunction尝试了这一点,我有一些时间安排真的让我抓狂。到目前为止,最糟糕的是使用本机sqrt()函数并让“智能”编译器“优化”。在24ns/float的情况下,使用x87 FPU,这是非常糟糕的:

inline float TestSqrtFunction(float-in)
{返回sqrt(in);}
我尝试的下一件事是使用内在函数强制编译器使用SSE的标量sqrt操作码:

inline void SSESqrt(float*限制pOut,float*限制pIn)
{
_mm_商店(pOut,mm_sqrt_ss(_mm_load_ss(pIn)));
//编译为MOVS、sqrtss、MOVS
}
这更好,为11.9ns/float。我也试过了,它比硬件运行得更好,为4.3ns/float,尽管误差为210分之一(这对我来说太大了)

最糟糕的是,我试着用SSE运算求平方根的倒数,然后用乘法得到平方根(x*1)/√x=√(十)。尽管这需要两个相关操作,但它是迄今为止最快的解决方案,为1.24ns/float,精确到2-14:

inline void SSESqrt\u Recip\u Times\u X(浮动*限制pOut,浮动*限制pIn)
{
__m128英寸=毫米负载不锈钢(销);
_商店(pOut,mm,mul,in,mm,rsqrt,in));
//编译为MOVS、movaps、rsqrtss、mulss、MOVS
}
我的问题基本上是什么为什么SSE内置于硬件平方根操作码的速度比用另外两个数学运算合成慢?

我确信这确实是op本身的成本,因为我已经证实:

  • 所有数据都适合缓存,并且 访问是顺序的
  • 函数是内联的
  • 展开循环没有任何区别
  • 编译器标志设置为完全优化(我检查了程序集是否良好)

edit:stephentyrone正确地指出,对长串数字的操作应使用向量化SIMD压缩运算,如
rsqrtps
——但此处的数组数据结构仅用于测试目的:我真正试图测量的是无法向量化的代码中使用的标量性能。)

sqrtss
给出正确的四舍五入结果
rsqrtss
给出了倒数的近似值,精确到大约11位

sqrtss
正在生成更精确的结果,用于需要精确性时<代码>rsqrtss
适用于近似值足够但需要速度的情况。如果你阅读英特尔的文档,你也会发现一个指令序列(平方根倒数近似,然后是一个牛顿-拉斐逊步长),它几乎给出了全部精度(如果我记得正确的话,大约23位精度),并且仍然比
sqrtss
快一些


编辑:如果速度非常关键,并且您实际上是在对许多值进行循环调用,那么您应该使用这些指令的向量化版本,
rsqrtps
sqrtps
,这两种指令都会对每条指令处理四个浮点。

而不是提供答案,这实际上可能是不正确的(我也不会检查或争论缓存和其他东西,假设它们是相同的)我会尝试向您指出可以回答您问题的来源。
区别可能在于sqrt和rsqrt的计算方式。您可以在这里阅读更多内容。我建议您从阅读您正在使用的处理器函数开始,这里有一些信息,特别是关于rsqrt的信息(cpu使用具有巨大近似值的内部查找表,这使得获得结果更加简单)。看起来,rsqrt比sqrt快得多,因此额外的一次mul操作(成本不高)可能不会改变这种情况

编辑:一些值得一提的事实:
1.有一次我对我的图形库进行了一些微优化,我使用了rsqrt来计算向量的长度(而不是sqrt,我用它的平方和乘以rsqrt,这正是你在测试中所做的),它的性能更好。
2.使用简单的查找表计算rsqrt可能更容易,对于rsqrt,当x为无穷大时,1/sqrt(x)为0,因此对于小x,函数值不会改变(很多),而对于sqrt,它为无穷大,所以这是简单的情况;)


另外,澄清:我不确定在我链接的书籍中我在哪里找到了它,但我非常确定我读到rsqrt使用了一些查找表,并且它应该只在结果不需要精确时使用,尽管-我可能也错了,就像前一段时间:)。

除法也是如此。MULSS(a,RCPSS(b))比DIVSS(a,b)快得多。事实上,即使你用牛顿-拉斐逊迭代法来提高它的精度,它仍然更快

英特尔和AMD都有
x' = 0.5 * x * (3 - n*x*x);
x[n] = b[0] * Y[0] * Y[1] * ... * Y[n]
y[n] = Y[0] * Y[1] * ... * Y[n]
b[i] = b[i-1] * Y[i-1]^2
Y[i] = 0.5 * (3 - b[i])
x[0] = n Y[0]
x[i] = x[i-1] * Y[i]
y[0] = Y[0]
y[i] = y[i-1] * Y[i]
Y[i] = 0.5 * (3 - x[i-1] * y[i-1])
     = 1 + 0.5 * (1 - x[i-1] * y[i-1])
x[i] = x[i-1] * (1 + 0.5 * (1 - x[i-1] * y[i-1]))
     = x[i-1] + x[i-1] * 0.5 * (1 - x[i-1] * y[i-1]))
y[i] = y[i-1] * (1 + 0.5 * (1 - x[i-1] * y[i-1]))
     = y[i-1] + y[i-1] * 0.5 * (1 - x[i-1] * y[i-1]))
r = 0.5 * (1 - x * y)
x' = x + x * r
y' = y + y * r
Y = approx_rsqrt(n)
x = Y * n
h = Y * 0.5
r = 0.5 - x * h
x' = x + x * r
h' = h + h * r