我想更详细地了解英特尔编译器使用的simd减少条款是如何工作的。
特别是对于形式
的循环double x = x_initial;
#pragma simd reduction(<operator1>:x)
for( int i = 0; i < N; i++ )
x <operator2> some_value;
我天真的猜测如下:
编译器为每个向量通道初始化x的私有副本,然后一次遍历循环一个向量宽度。例如,如果矢量宽度是4倍,则这将对应于N / 4次迭代加上最后的剥离循环。在迭代的每个步骤中,使用operator2
更新每个通道的x的私有副本,然后在最后,使用4个向量通道&#39}。私人副本使用operator1
合并。 auto-vectorization guide似乎没有直接解决此问题。
我做了一些实验,发现一些结果符合我的期望,有些结果不符合我的预期。例如,我尝试了这个案例
double x = 1;
#pragma simd reduction(*:x) assert
for( int i = 0; i < 16; i++ )
x += a[i]; // All elements of a are equal to 3.0
cout << "x after (*:x), x += a[i] loop: " << x << endl;
其中operator1
为*且operator2
为+ =。当我为avx2编译时,其矢量宽度为4倍,输出为28561 =(1 + 4 * a [i])^ 4。这意味着代码首先将x的4个通道专用副本初始化为1,然后将4个双宽矢量通道迭代跨越16个行程计数,每个副本增加3个4次。每个通道专用副本x现在等于13.最后,使用operator2
组合(减少)车道 - 私人副本,即*,产生13 * 13 * 13 * 13 = 28561.
但是,当我切换*和+运算符时,就像这样
x = 1;
#pragma simd reduction(+:x) assert
for( int i = 0; i < 16; i++ )
x *= a[i];
cout << "x after (+:x), x *= a[i] loop: " << x << endl;
再次为avx2编译,输出为1.0。如果我的理论是正确的,那么每个向量通道最终应该包含1 * 3 ^ 4的值,然后使用+组合得到4 * 3 ^ 4 = 324.显然情况并非如此。我错过了什么?