我的代码在很大程度上依赖于计算3D空间中两点之间的距离.为了避免昂贵的平方根,我使用整个平方距离.但它仍然占用了计算时间的很大一部分,我想用更快的东西替换我的简单函数.我现在有:
double distance_squared(double *a, double *b)
{
double dx = a[0] - b[0];
double dy = a[1] - b[1];
double dz = a[2] - b[2];
return dx*dx + dy*dy + dz*dz;
}
Run Code Online (Sandbox Code Playgroud)
我也尝试使用宏来避免函数调用,但它没有多大帮助.
#define DISTANCE_SQUARED(a, b) ((a)[0]-(b)[0])*((a)[0]-(b)[0]) + ((a)[1]-(b)[1])*((a)[1]-(b)[1]) + ((a)[2]-(b)[2])*((a)[2]-(b)[2])
Run Code Online (Sandbox Code Playgroud)
我想过使用SIMD指令但是找不到一个好的例子或完整的指令列表(理想情况下是一些乘法+加两个向量).
GPU不是一个选项,因为每个函数调用只知道一组点.
计算距离平方的最快方法是什么?
我有一个__m256d向量,包含四个64位浮点值.
我需要找到向量元素的水平最大值,并将结果存储在双精度标量值中;
我的尝试最终都使用了很多矢量元素的改组,使得代码不是很优雅也没有效率.此外,我发现不可能只留在AVX域.在某些时候,我不得不使用SSE 128位指令来提取最终的64位值.但是,我想在最后的声明中被证明是错误的.
因此理想的解决方案将:
1)仅使用AVX指令.
2)最小化指令数量.(我希望不超过3-4条说明)
话虽如此,任何优雅/高效的解决方案都将被接受,即使它不符合上述指导原则.
谢谢你的帮助.
-Luigi
我正在为SSE和AVX寻找SIMD数学库(最好是开源).我的意思是,例如,如果我有一个具有8个浮点值的AVX寄存器v,我希望sin(v)一次返回所有八个值的sin.
AMD有一个propreitery库,LibM http://developer.amd.com/tools/cpu-development/libm/,它有一些SIMD数学函数,但如果它检测到Intel CPU没有的FMA4,LibM只使用AVX.另外我不确定它是否完全使用AVX,因为所有的功能名称都以s4(d2)而不是s8(d4)结尾.它提供了比英特尔CPU上的标准数学库更好的性能,但它并没有好多少.
英特尔将SVML作为其C++编译器的一部分,但编译器套件在Windows上非常昂贵.此外,英特尔还削弱了非英特尔CPU上的库.
我找到了以下AVX库,http://software-lisc.fbk.eu/avx_mathfun/,它支持一些数学函数(exp,log,sin,cos和sincos).它为我提供了非常快的结果,比SVML更快,但我没有检查准确性.它仅适用于单个浮点,并且在Visual Studio中不起作用(尽管这很容易修复).它基于另一个SSE库.
有没有人有任何其他建议?
编辑:我发现一个SO线程有很多关于这个主题的答案 Vectorized Trig函数在C?
通过制作四个4x4矩阵并转置每个矩阵,可以实现8x8矩阵的转置.这不是我想要的.
在另一个问题中,一个答案提供了一个解决方案,只需要24个8x8矩阵指令.但是,这不适用于花车.
由于AVX2包含256位寄存器,因此每个寄存器适合8个32位整数(浮点数).但问题是:
如何使用AVX/AVX2转换8x8浮点矩阵,尽可能使用最小的指令?
我知道3种方法,但据我所知,通常只使用前2种方法:
使用andps或屏蔽符号位andnotps.
将值从零减去否定,然后得到原始的最大值并否定.
subps完成才能使用该maxps指令.与选项2类似,将原始值从零减去否定,但随后使用原始值"按位"和"按位" andps.我运行了一个测试,将其与方法2进行比较,除了处理NaNs 之外,它似乎与方法2的行为相同,在这种情况下,结果将NaN与方法2的结果不同.
andps通常比速度快maxps.NaN涉及到s 时,这是否会导致任何意外行为?也许不是,因为a NaN仍然是a NaN,即使它是一个不同的值NaN,对吧?欢迎提出想法和意见.
AXV2没有任何整数乘法,其源大于32位.它提供32 x 32 - > 32乘法,以及32 x 32 - > 64乘以1,但没有64位源.
假设我需要一个输入大于32位但小于或等于52位的无符号乘法 - 我可以简单地使用浮点DP乘法或FMA指令,并且当整数输入和输出时输出将是位精确的结果可以用52或更少的比特表示(即,在[0,2 ^ 52-1]范围内)?
如果我想要产品的所有104位更一般的情况怎么样?或整数乘积超过52位的情况(即,产品在位索引中的非零值> 52) - 但我只想要低52位?在后一种情况下,它MUL会给我更高的位并舍去一些低位(也许这就是IFMA帮助的?).
编辑:事实上,根据这个答案,也许它可以做任何高达2 ^ 53的事情- 我忘记了1在尾数之前隐含的领先有效地给了你一点.
1有趣的是,正如Mysticial 在评论中所解释的那样,64位产品PMULDQ操作的延迟是32位PMULLD版本的一半,吞吐量是32位版本的两倍.
__m256 dst = _mm256_cmp_ps(value1, value2, _CMP_LE_OQ);
Run Code Online (Sandbox Code Playgroud)
如果dst是,[0,0,0,-nan, 0,0,0,-nan];
我希望能够知道第一个-nan索引,在这种情况下,3无需进行for循环8迭代。这可能吗?
我试图在Intel i3处理器上找到32个元素(每个1字节数据)的总和减少量.我这样做了:
s=0;
for (i=0; i<32; i++)
{
s = s + a[i];
}
Run Code Online (Sandbox Code Playgroud)
但是,由于我的应用程序是一个需要更少时间的实时应用程序,因此需要花费更多时间.请注意,最终金额可能超过255.
有没有办法可以使用低级SIMD SSE2指令实现这一点?不幸的是我从未使用过SSE.我试图为此目的搜索sse2函数,但它也不可用.(sse)是否可以保证减少这种小型问题的计算时间?
有什么建议??
注意:我已经使用OpenCL和CUDA实现了类似的算法,虽然问题规模很大,但效果很好.对于小型问题,开销成本更高.不确定它在SSE上是如何工作的
查看AVX2内在函数文档,收集了加载指令,例如VPGATHERDD:
__m128i _mm_i32gather_epi32 (int const * base, __m128i index, const int scale);
Run Code Online (Sandbox Code Playgroud)
从文档中我不清楚的是计算的加载地址是 元素地址还是字节地址,即元素的加载地址i:
load_addr = base + index[i] * scale; // (1) element addressing ?
Run Code Online (Sandbox Code Playgroud)
要么:
load_addr = (char *)base + index[i] * scale; // (2) byte addressing ?
Run Code Online (Sandbox Code Playgroud)
从英特尔文档看起来它可能是(2),但是这没有多大意义,因为聚集的负载的最小元素大小是32位 - 为什么要从未对齐的地址加载(即使用比例<4) )?