有人看到下面的循环代码有什么明显的地方吗?我没有看到为什么as 2012的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[]周围的父类都是函数调用的残余,我在这里省略了这个函数调用,因为如果没有函数调用(最简单的形式),循环仍然无法自动向量化。
编辑:
我已经将循环简化为它的最简单的形式:
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]);
}发布于 2012-12-23 01:12:13
我们只想说有几件事能阻止这个循环的矢量化.
考虑到这一点:
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,即循环携带的依赖项。1203:
循环体包括对数组的非连续访问。好吧..。为什么有不同的原因?我不知道。但这一次,原因是正确的。
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);
}免责声明:这是未经测试和忽略对齐。
发布于 2012-12-23 01:11:57
从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。
https://stackoverflow.com/questions/14007805
复制相似问题