GCC自动矢量化对运行时没有任何影响,即使它被认为是有利可图的"

时间:2014-11-15 12:46:04

标签: c gcc auto-vectorization

过去几天我一直在阅读gcc 4.7的自动向量化。我按照我在网上看到的一些例子,设置似乎是正确的。但是当我实际运行代码并在矢量化开启或关闭之间进行比较时,运行时没有明显的差异。

以下是我一直在使用的代码:

#include <string.h>
#include <stdlib.h>
#include <emmintrin.h>
#include <stdio.h>
#include <math.h>

int main(int argc, char** argv) {

    long b = strtol(argv[2], NULL, 0); 
    unsigned long long int i;
    unsigned long long int n = (int)pow(2,29);                                                                                                                                                                                            
    float total = 0;

    float *__restrict__ x1; 
    float *__restrict__ y1; 

    posix_memalign((void *)&x1, 16, sizeof(float)*n);
    posix_memalign((void *)&y1, 16, sizeof(float)*n);


    float *__restrict__ x = __builtin_assume_aligned(x1,16);
    float *__restrict__ y = __builtin_assume_aligned(y1,16);

    for (i=0;i<n;i++) {
            x[i] = i;
            y[i] = i;
    }   

    for (i=0; i<n; i++) {
            y[i] += x[i];
    }   

    printf("y[%li]: \t\t\t\t%f\n",  b,y[b]);
    printf("correct answer: \t\t\t%f\n", (b)*2);
    return 0;
}

这些东西对我来说似乎是多余的,但是必须让编译器了解发生了什么(尤其是数据已对齐的事实)。从命令行读取的“b”变量就在那里,因为我对编译器完全优化掉循环感到偏执。

启用vectorizeration时,这是编译器命令:

gcc47 -ftree-vectorizer-verbose=3 -msse2 -lm -O2 -finline-functions -funswitch-loops -fpredictive-commoning -fgcse-after-reload -fipa-cp-clone test.c -ftree-vectorize -o v

基本上,这相当于只使用-O3。我把旗帜放在自己身上,这样我所需要做的就是删除“ftree-vectorize”并能够测试结果没有矢量化。

这是ftree-vectorize-verbose标志的输出,以显示代码实际上是向量化的:

Analyzing loop at test.c:29

29: vect_model_load_cost: aligned.
29: vect_model_load_cost: inside_cost = 1, outside_cost = 0 .
29: vect_model_load_cost: aligned.
29: vect_model_load_cost: inside_cost = 1, outside_cost = 0 .
29: vect_model_simple_cost: inside_cost = 1, outside_cost = 0 .
29: vect_model_store_cost: aligned.
29: vect_model_store_cost: inside_cost = 1, outside_cost = 0 .
29: cost model: Adding cost of checks for loop versioning aliasing.

29: Cost model analysis: 
  Vector inside of loop cost: 4
  Vector outside of loop cost: 4
  Scalar iteration cost: 4
  Scalar outside cost: 1
  prologue iterations: 0
  epilogue iterations: 0
  Calculated minimum iters for profitability: 2

29:   Profitability threshold = 3


Vectorizing loop at test.c:29

29: Profitability threshold is 3 loop iterations.
29: created 1 versioning for alias checks.

29: LOOP VECTORIZED.
Analyzing loop at test.c:24

24: vect_model_induction_cost: inside_cost = 2, outside_cost = 2 .
24: vect_model_simple_cost: inside_cost = 2, outside_cost = 0 .
24: not vectorized: relevant stmt not supported: D.5806_18 = (float) D.5823_58;

test.c:7: note: vectorized 1 loops in function.

请注意,3次迭代后矢量化是有利可图的,而且我正在运行2 ^ 29~ = 500,000,000次迭代。所以我应该期待关闭矢量化的运行时差异很大,对吗?

嗯,这是代码的运行时(我连续运行了20次):

59.082s                                                                                                                                                                                                                                       
79.385s
57.557s
57.264s
53.588s
54.300s
53.645s
69.044s
57.238s
59.366s
56.314s
55.224s
57.308s
57.682s
56.083s
369.590s
59.963s
55.683s
54.979s
62.309s

抛弃奇怪的~370s异常值,得出平均运行时间为58.7s,标准偏差为6.0s。

接下来,我将使用与之前相同的命令进行编译,但没有-ftree-vectorize标志:

gcc47 -ftree-vectorizer-verbose=3 -msse2 -lm -O2 -finline-functions -funswitch-loops -fpredictive-commoning -fgcse-after-reload -fipa-cp-clone test.c -o nov

再次连续运行程序20次会产生以下时间:

69.471s                                                                                                                                                                                                                                       
57.134s
56.240s
57.040s
55.787s
56.530s
60.010s
60.187s
324.227s
56.377s
55.337s
54.110s
56.164s
59.919s
493.468s
63.876s
57.389s
55.553s
54.908s
56.828s

再次抛弃异常值,这意味着平均运行时间为57.9s,标准偏差为3.6s。

因此,这两个版本在统计上无法区分运行时。

有人能指出我做错了什么吗?编译器吐出的“盈利门槛”是不是意味着我的意思?我真的很感激人们可以给我的任何帮助,我一直试图在过去一周内解决这个问题。

修改

我实施了@nilspipenbrinck建议的更改,似乎有效。我将矢量化循环卡在一个函数中,并将该函数称为多次。对于无矢量化,相对运行时间现在为24.0s(西格玛<0.1s),而对于矢量化,相对运行时间为20.8s(西格玛<0.2s),或者速度提高13%。没有我希望的那么多,但至少现在我知道它的工作!感谢您花时间查看我的问题并写下答案,我真的很感激。

2 个答案:

答案 0 :(得分:4)

你没有多少算术。因此,测试代码的运行时是内存限制的。例如。你花了大部分时间在CPU和内存之间移动数据。

此外,你的n非常大,有2 ^ 29个元素。因此,您无法以任何方式从第一级和第二级缓存中受益。

如果要查看SSE的改进,请使用较小的n,以便只触及8或16千字节的数据。还要确保数据是热的&#39;例如它最近被CPU访问过。这样一来,数据就不必从主内存中移出,而是从缓存中移动,速度要快几倍。

作为替代方案,您还可以执行 lot 更多算术。这将使内存预取系统有机会在您利用CPU进行数学运算时从后台的主内存中获取数据。

总结:如果算术比你的系统更快,你可以移动内存,你将看不到任何好处。内存访问时间将成为瓶颈,使用SSE指令集保存的几个周期将在内存访问时序的噪声中丢失。

答案 1 :(得分:2)

有几个因素决定了矢量化代码的盈利方式。在这种情况下(基于你提供的输出),编译器只是向量化一个循环,我认为这是第二个循环因为第一个循环通常会被忽略,因为没有足够的计算来使它有利于向量化

您发布的运行时间是针对整个代码的,而不是单独的循环,因此只有这么多的矢量化将对整个运行时间起作用。如果你真的想看看矢量化有多大的改进,我建议运行一个分析器,如AMD Code XL,Intel Vtune,OProfile等,它会告诉你具体的那个循环在时间和性能方面有多大改进正在制作。

现在我正在评估矢量化编译器,我会使用矢量化将代码运行速度提高60倍,其他时候加速并不那么令人印象深刻,而且这一切都取决于循环,编译器和架构你正在使用。