有没有人看到下面关于循环代码的任何明显内容,我还没有看到VS2012的C ++编译器无法自动向量化的原因?
当我使用info C5002: loop not vectorized due to reason '1200'
命令行开关时,所有编译器都给我/Qvec-report:2
。
原因1200在MSDN中记录为:
循环包含阻止循环的数据依赖性 矢量。循环的不同迭代会干扰每个循环 其他这样,矢量化循环会产生错误的答案,并且 自动矢量化器无法证明自己没有这样的数据 依赖性。
我知道(或者我很确定)没有任何循环数据依赖性,但我不确定是什么阻止编译器实现这一点。
这些source
和dest
指针不会重叠,也不会为同一个内存添加别名,我试图通过__restrict
向编译器提供该提示。
pitch
始终为正整数值,类似4096
,具体取决于屏幕分辨率,因为这是一个8bpp-> 32bpp渲染/转换函数,逐列操作。
byte * __restrict source;
DWORD * __restrict dest;
int pitch;
for (int i = 0; i < count; ++i) {
dest[(i*2*pitch)+0] = (source[(i*8)+0]);
dest[(i*2*pitch)+1] = (source[(i*8)+1]);
dest[(i*2*pitch)+2] = (source[(i*8)+2]);
dest[(i*2*pitch)+3] = (source[(i*8)+3]);
dest[((i*2+1)*pitch)+0] = (source[(i*8)+4]);
dest[((i*2+1)*pitch)+1] = (source[(i*8)+5]);
dest[((i*2+1)*pitch)+2] = (source[(i*8)+6]);
dest[((i*2+1)*pitch)+3] = (source[(i*8)+7]);
}
每个source[]
周围的parens是函数调用的残余,我在这里已经省略了,因为循环仍然无法以最简单的形式自动向量化而没有函数调用。
修改
我已经将循环简化为最简单的形式,我可以:
for (int i = 0; i < 200; ++i) {
dest[(i*2*4096)+0] = (source[(i*8)+0]);
}
这仍然会产生相同的1200原因代码。
编辑(2):
这个具有本地分配和相同指针类型的最小测试用例仍然无法自动向量化。我在这一点上感到困惑。
const byte * __restrict source;
byte * __restrict dest;
source = (const byte * __restrict ) new byte[1600];
dest = (byte * __restrict ) new byte[1600];
for (int i = 0; i < 200; ++i) {
dest[(i*2*4096)+0] = (source[(i*8)+0]);
}
答案 0 :(得分:11)
让我们说,不仅仅是阻止这个循环进行矢量化的几件事......
考虑一下:
int main(){
byte *source = new byte[1000];
DWORD *dest = new DWORD[1000];
for (int i = 0; i < 200; ++i) {
dest[(i*2*4096)+0] = (source[(i*8)+0]);
}
for (int i = 0; i < 200; ++i) {
dest[i*2*4096] = source[i*8];
}
for (int i = 0; i < 200; ++i) {
dest[i*8192] = source[i*8];
}
for (int i = 0; i < 200; ++i) {
dest[i] = source[i];
}
}
编译器输出:
main.cpp(10) : info C5002: loop not vectorized due to reason '1200'
main.cpp(13) : info C5002: loop not vectorized due to reason '1200'
main.cpp(16) : info C5002: loop not vectorized due to reason '1203'
main.cpp(19) : info C5002: loop not vectorized due to reason '1101'
让我们打破这个:
前两个循环是相同的。因此,他们给出原始原因1200
,这是循环携带的依赖。
第3个循环与第2个循环相同。然而,编译器提供了不同的原因1203
:
循环体包括对数组的非连续访问
好的...为什么有不同的原因?我不知道。但这一次,原因是正确的。
第4个循环给出了1101
:
循环包含不可向量化的转换操作(可能是隐式的)
因此,VC ++不够智能,无法发出SSE4.1 pmovzxbd
指令。
这是一个非常小众的案例,我不希望任何现代编译器能够做到这一点。如果可以的话,你需要指定SSE4.1。
所以唯一不寻常的是初始循环报告循环携带依赖的原因。
嗯,这是一个艰难的调用......我会说到目前为止编译器只是没有发出正确的理由。 (当它真的应该是非连续访问时。)
回到这一点,我不希望MSVC或任何编译器能够对原始循环进行矢量化。您的原始循环具有以4的块分组的访问 - 这使其连续足以进行矢量化。但是期望编译器能够识别它是一件很长的事。
所以如果重要的话,我建议手动矢量化这个循环。您需要的内在因素是_mm_cvtepu8_epi32()
。
原始循环:
for (int i = 0; i < count; ++i) {
dest[(i*2*pitch)+0] = (source[(i*8)+0]);
dest[(i*2*pitch)+1] = (source[(i*8)+1]);
dest[(i*2*pitch)+2] = (source[(i*8)+2]);
dest[(i*2*pitch)+3] = (source[(i*8)+3]);
dest[((i*2+1)*pitch)+0] = (source[(i*8)+4]);
dest[((i*2+1)*pitch)+1] = (source[(i*8)+5]);
dest[((i*2+1)*pitch)+2] = (source[(i*8)+6]);
dest[((i*2+1)*pitch)+3] = (source[(i*8)+7]);
}
矢量化如下:
for (int i = 0; i < count; ++i) {
__m128i s0 = _mm_loadl_epi64((__m128i*)(source + i*8));
__m128i s1 = _mm_unpackhi_epi64(s0,s0);
*(__m128i*)(dest + (i*2 + 0)*pitch) = _mm_cvtepu8_epi32(s0);
*(__m128i*)(dest + (i*2 + 1)*pitch) = _mm_cvtepu8_epi32(s1);
}
免责声明:这是未经测试的,并且忽略了对齐。
答案 1 :(得分:2)
从MSDN文档中,报告错误1203的情况
void code_1203(int *A)
{
// Code 1203 is emitted when non-vectorizable memory references
// are present in the loop body. Vectorization of some non-contiguous
// memory access is supported - for example, the gather/scatter pattern.
for (int i=0; i<1000; ++i)
{
A[i] += A[0] + 1; // constant memory access not vectorized
A[i] += A[i*2+2] + 2; // non-contiguous memory access not vectorized
}
}
这可能是索引的计算与自动矢量化器混乱。有趣的是,显示的错误代码不是1203。