由于“1300”原因,循环未矢量化

问题描述 投票:0回答:3

我正在尝试向量化一个进行大量迭代(超过 300 000 000 次)的循环以获得一些计算时间:

uint16_t* samples = (uint16_t*)pixmap->samples;

Image image(pixmap->w, pixmap->h);
uint8_t *dest = (uint8_t*)image[0];

for (int x = 0; x < len; x++)
{
    dest[x] = samples[x] & 0xFF;
}

但是 qvec-report 说由于原因 1300,它无法矢量化。

根据MSDN,原因 1300 是:

循环体不包含或很少包含计算。

确实,我的循环体执行的计算很少,但由于迭代很多,所以仍然需要一些时间。

为什么在这种情况下没有完成矢量化?是因为不值得吗?如果是,为什么?

如果没有,有什么方法或技巧可以“强制”它吗?

c++ vectorization
3个回答
3
投票

基本上,循环体非常简单,因此按原样进行编译比对其进行矢量化更有效,因为矢量化的运行时成本会比按原样执行代码更大。

尝试强制它确实没有意义,因为编译器告诉您矢量化版本的效率低于非矢量化版本。如果您向循环添加更多计算,编译器可能会选择对其进行向量化。


3
投票

您可能会混淆自动矢量化器(SSE/AVX)和自动并行化器(线程)。但这不是真正的问题,我怀疑:

dest[x] = samples[x] & 0xFF
真正的意思是
dest[x] = static_cast<uint8_t>(static_cast<int>(samples[x]) & 0xFF)
。由于所有这些转换为不同的宽度,很难想出等效的 SSE 代码。 SSE2 允许您以 16x8 或 8x16 位组织 128 位寄存器,但在这里您混合了 8 和 16 位类型以及 32 位文字。


0
投票

矢量化通常并不完美,并且错过了应该矢量化的东西。

它确实没有“计算”。但即使是简单的内存移动也可以矢量化,这样做是有道理的。在您的情况下,存在元素缩小,这可以在具有许多元素的并行指令中同时完成,并且元素非常小。

所以这个函数确实是一个很好的向量化候选函数,如果不是,那么它就是一个错过的优化。

我已经让你的重现变得独立,用多个版本的MSVC在Godbolt上编译它,并观察到它从17.10开始矢量化https://godbolt.org/z/5fqcP61M1


当您希望向量化的函数未向量化时您可以执行的操作:

  • 尝试其他编译器,就像我在 Godbolt 上所做的那样,但也包括 clang 和 gcc。如果没有编译器对此进行向量化,那么这个要求可能就太多了。
  • 如果 clang 向量化,您可以将 MSVC 项目的部分内容切换到 clang 工具集。 Clang-cl 与 MSVC 具有良好的兼容性。
  • 如果 MSVC 不进行矢量化,但您认为应该进行矢量化,您可以将缺失的优化以及其他编译器错误报告给 https://developercommunity.visualstudio.com/cpp
  • 如果您要使用的编译器没有对某些内容进行向量化,或者做得不是很好,但您知道如何执行此操作,则可以继续使用编译器内在函数手动执行此操作。
© www.soinside.com 2019 - 2024. All rights reserved.