使用GCC进行向量化失败

3

我正在尝试理解向量化,但令我惊讶的是这个非常简单的代码没有被向量化

#define n 1024
int main () {
  int i, a[n], b[n], c[n];

  for(i=0; i<n; i++) { a[i] = i; b[i] = i*i; }
  for(i=0; i<n; i++) c[i] = a[i]+b[i];
}

在某些情况下,英特尔编译器会对初始化循环(第5行)进行向量化处理。
> icc -vec-report a.c
a.c(5): (col. 3) remark: LOOP WAS VECTORIZED

使用GCC编译器,似乎没有任何输出。
> gcc -ftree-vectorize -ftree-vectorizer-verbose=2 a.c

我是否做错了什么?这不应该是一个非常简单的可向量化循环吗?所有相同的操作,连续的内存等。我的CPU支持SSE1/2/3/4。

--- 更新 ---

根据下面的答案,这个例子对我来说有效。

#include <stdio.h>
#define n 1024

int main () {
  int i, a[n], b[n], c[n];

  for(i=0; i<n; i++) { a[i] = i; b[i] = i*i; }
  for(i=0; i<n; i++) c[i] = a[i]+b[i];

  printf("%d\n", c[1023]);  
}

使用ICC

> icc -vec-report a.c
a.c(7): (col. 3) remark: LOOP WAS VECTORIZED
a.c(8): (col. 3) remark: LOOP WAS VECTORIZED

并且 gcc

> gcc -ftree-vectorize -fopt-info-vec -O a.c
a.c:8:3: note: loop vectorized
a.c:7:3: note: loop vectorized

3
或许是GCC干脆把所有东西都移除了。 - Mysticial
使用 -O3 选项来编译 gcc 以获取更多信息。 - ojs
2
你应该从程序外部传入 n,以确保优化器不仅计算最终结果并删除循环。 - Zan Lynx
2个回答

8

我稍微修改了您的源代码,以确保GCC无法删除循环:

#include <stdio.h>
#define n 1024

int main () {
  int i, a[n], b[n], c[n];

  for(i=0; i<n; i++) { a[i] = i; b[i] = i*i; }
  for(i=0; i<n; i++) c[i] = a[i]+b[i];

  printf("%d\n", c[1023]);  
}

GCC(v4.8.2)可以向量化这两个循环,但需要使用 -O 标志:

gcc -ftree-vectorize -ftree-vectorizer-verbose=1 -O2 a.c

结果如下:

分析 a.c:8 处的循环

a.c:8 的循环向量化

a.c:8 注意: 循环已向量化。分析 a.c:7 处的循环

a.c:7 的循环向量化

a.c: 注意:函数中向量化了 2 个循环

使用 -fdump-tree-vect 开关,GCC 将在 a.c.##t.vect 文件中输出更多信息(这对理解“内部”发生的事情非常有用)。

另外,请注意以下几点:


看起来像魔法,但它确实有效。我必须(首先)添加printf,我想这样编译器就不会将其视为无用变量。然后我必须在编译器选项中添加-O和-fopt-info-vec。我的编译器(GCC 4.9.0)不显示任何内容,使用-ftree-vectorizer-verbose=<n>。如果用手册中的所有选项替换-O,则什么也不做! - jaap
2
请注意,编译器仍然可以删除您的循环,因为它可以在编译期间运行循环并替换结果。 - Zan Lynx

1
大多数情况下,如果您的处理器支持,选项-Ofast -march=native将矢量化您的代码。
$ gcc compute_simple.c -Ofast -march=native -fopt-info-vec -o compute_simple.bin
compute_simple.c:14:5: note: loop vectorized
compute_simple.c:14:5: note: loop versioned for vectorization because of possible aliasing
compute_simple.c:14:5: note: loop vectorized

要知道您的处理器是否支持此功能,请使用lscpu命令并查看可用标志。

$ lscpu
Architecture:        x86_64
CPU op-mode(s):      32-bit, 64-bit
Byte Order:          Little Endian
CPU(s):              12
...
Vendor ID:           GenuineIntel
...
Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge  
 mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall   
nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl   
xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64   
monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1   
sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand   
lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb   
stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1   
hle avx2 smep bmi2 erms invpcid rtm mpx rdseed adx smap clflushopt intel_pt   
xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify   
hwp_act_window hwp_epp md_clear flush_l1d

在英特尔上需要sse/avx,在ARM上需要neon,在AMD(如xop)等其他平台上则需要其他技术。

您可以通过搜索gcc文档来获取更多有关向量化的信息。

这里有一篇关于该主题的不错文章,其中提供了可用于许多平台的标志: https://gcc.gnu.org/projects/tree-ssa/vectorization.html

最后,如上所述,在旧版本的gcc中使用-ftree-vectorizer-verbose=n,在较新版本中使用-fopt-info-vec/-fopt-info-vec-missed以查看哪些内容已进行向量化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接