【发布时间】:2015-05-19 07:17:28
【问题描述】:
我只是想检查优化一些基本例程的最佳方法。在这种情况下,我尝试了将 2 个浮点向量相乘的非常简单的示例:
void Mul(float *src1, float *src2, float *dst)
{
for (int i=0; i<cnt; i++) dst[i] = src1[i] * src2[i];
};
Plain C 实现非常慢。我使用 AVX 做了一些外部 ASM,也尝试使用内部函数。以下是测试结果(时间,越小越好):
ASM: 0.110
IPP: 0.125
Intrinsics: 0.18
Plain C++: 4.0
(使用MSVC 2013,SSE2编译,试用Intel Compiler,结果差不多)
正如您所见,我的 ASM 代码甚至击败了英特尔性能基元(可能是因为我做了很多分支以确保我可以使用 AVX 对齐指令)。但我个人更喜欢使用内在方法,它更容易管理,我认为编译器应该在优化所有分支和东西方面做得最好(我的 ASM 代码在这方面很糟糕,但它更快)。所以这里是使用内在函数的代码:
int i;
for (i=0; (MINTEGER)(dst + i) % 32 != 0 && i < cnt; i++) dst[i] = src1[i] * src2[i];
if ((MINTEGER)(src1 + i) % 32 == 0)
{
if ((MINTEGER)(src2 + i) % 32 == 0)
{
for (; i<cnt-8; i+=8)
{
__m256 x = _mm256_load_ps( src1 + i);
__m256 y = _mm256_load_ps( src2 + i);
__m256 z = _mm256_mul_ps(x, y);
_mm256_store_ps(dst + i, z);
};
}
else
{
for (; i<cnt-8; i+=8)
{
__m256 x = _mm256_load_ps( src1 + i);
__m256 y = _mm256_loadu_ps( src2 + i);
__m256 z = _mm256_mul_ps(x, y);
_mm256_store_ps(dst + i, z);
};
};
}
else
{
for (; i<cnt-8; i+=8)
{
__m256 x = _mm256_loadu_ps( src1 + i);
__m256 y = _mm256_loadu_ps( src2 + i);
__m256 z = _mm256_mul_ps(x, y);
_mm256_store_ps(dst + i, z);
};
};
for (; i<cnt; i++) dst[i] = src1[i] * src2[i];
简单:首先到达 dst 与 32 字节对齐的地址,然后分支检查哪些源对齐。
一个问题是 C++ 实现在开头和结尾都没有使用 AVX,除非我在编译器中启用了 AVX,这是我不想要的,因为这应该只是 AVX 专业化,但软件应该可以在一个平台,其中 AVX 不可用。遗憾的是,vmovss 等指令似乎没有内在函数,因此将 AVX 代码与编译器使用的 SSE 混合可能会受到惩罚。但是,即使我在编译器中启用了 AVX,它仍然没有低于 0.14。
任何想法如何优化这一点以使 instrisics 达到 ASM 代码的速度?
【问题讨论】:
-
您可以只编译启用了 avx 的专业化文件。
-
我们为什么不直接将一个小块 memcpy 到一个已知对齐的位置,然后 memcpy 到真正的 dst?
-
你的编译选项是什么?
-
您可能不会将苹果与苹果进行比较(请参阅 Hurkyl 的回答)。看看组装。
-
出于兴趣,您的数组大小是多少,典型的对齐用例是什么(如果有的话),以及您的时序数字(例如内在函数的 0.18)代表什么?
标签: c++ assembly sse intrinsics avx