选项 -ftree-slp-transpose-vectorize
该选项在循环拆分阶段,增强对存在连续访存读的循环的数据流分析能力,通过插入临时数组拆分循环;SLP矢量化阶段,新增对grouped_stores进行转置的SLP分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | int foo (unsigned char *oxa, int ia, unsigned char *oxb, int ib) { unsigned tmp[4][4]; unsigned a0, a1, a2, a3; int sum = 0; for (int i = 0; i < 4; i++, oxa += ia, oxb += ib) { a0 = (oxa[0] - oxb[0]) + ((oxa[4] - oxb[4]) << 16); a1 = (oxa[1] - oxb[1]) + ((oxa[5] - oxb[5]) << 16); a2 = (oxa[2] - oxb[2]) + ((oxa[6] - oxb[6]) << 16); a3 = (oxa[3] - oxb[3]) + ((oxa[7] - oxb[7]) << 16); int t0 = a0 + a1; int t1 = a0 - a1; int t2 = a2 + a3; int t3 = a2 - a3; tmp[i][0] = t0 + t2; tmp[i][2] = t0 - t2; tmp[i][1] = t1 + t3; tmp[i][3] = t1 - t3; } for (int i = 0; i < 4; i++) { int t0 = tmp[0][i] + tmp[1][i]; int t1 = tmp[0][i] - tmp[1][i]; int t2 = tmp[2][i] + tmp[3][i]; int t3 = tmp[2][i] - tmp[3][i]; a0 = t0 + t2; a2 = t0 - t2; a1 = t1 + t3; a3 = t1 - t3; sum += a0 + a1 + a2 + a3; } return sum; } |
对于如上所示的用例,针对第一个for循环,可以拆分为如下形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | for (int i = 0; i < 4; i++, oxa += ia, oxb += ib) { a00[i] = (oxa[0] - oxb[0]) + ((oxa[4] - oxb[4]) << 16); a11[i] = (oxa[1] - oxb[1]) + ((oxa[5] - oxb[5]) << 16); a22[i] = (oxa[2] - oxb[2]) + ((oxa[6] - oxb[6]) << 16); a33[i] = (oxa[3] - oxb[3]) + ((oxa[7] - oxb[7]) << 16); } for (int i = 0; i < 4; i++) { int t0 = a00[i] + a11[i]; int t1 = a00[i] - a11[i]; int t2 = a22[i] + a33[i]; int t3 = a22[i] - a33[i]; tmp[i][0] = t0 + t2; tmp[i][2] = t0 - t2; tmp[i][1] = t1 + t3; tmp[i][3] = t1 - t3; } |
再针对于拆分所得的第一个循环,等号右边的计算同构且为连续load可以矢量化,但是由于左边a00[i]、a11[i]、a22[i]、a33[i]的内存地址不连续,无法作为矢量化SLP树的根节点,因此失去这种场景的矢量化机会。 在a00[i]、a11[i]、a22[i]、a33[i]写入内存时,期望寄存器中的内容为:
register |
values |
---|---|
vec0 |
a00[0] a00[1] a00[2] a00[3] |
vec1 |
a11[0] a11[1] a11[2] a11[3] |
vec2 |
a22[0] a22[1] a22[2] a22[3] |
vec3 |
a33[0] a33[1] a33[2] a33[3] |
而每次迭代内可以计算得到寄存器的内容为:
register |
values |
---|---|
vec0 |
a00[0] a11[0] a22[0] a33[0] |
vec1 |
a00[1] a11[1] a22[1] a33[1] |
vec2 |
a00[2] a11[2] a22[2] a33[2] |
vec3 |
a00[3] a11[3] a22[3] a33[3] |
将grouped_store进行转置,即可得到预期的SLP树根节点。再利用SLP原有的能力,进行后续的矢量化分析。
此外,针对拆分所得的第二个循环以及示例中的最后一个循环,tmp二维数组存在写入内存后立刻读取的行为,针对这种场景,将访存行为优化为寄存器之间的permutation行为。该访存优化默认开启。
使用方法
在选项中加入:
1 | -O3 -ftree-slp-transpose-vectorize
|
-ftree-slp-transpose-vectorize选项,需要在-O3开启的基础上才使能。