4

我正在尝试对一些简单的计算进行矢量化,以加快 SIMD 架构的速度。但是,我也想将它们作为内联函数,因为函数调用和非向量化代码也需要计算时间。但是,我不能总是同时实现它们。事实上,我的大多数内联函数都无法自动矢量化。这是一个有效的简单测试代码:

inline void add1(double *v, int Length) {
    for(int i=0; i < Length; i++) v[i] += 1;
}

void call_add1(double v[], int L) {
    add1(v, L);
}

int main(){return 0;}

在 Mac OS X 10.12.3 上,编译它:

clang++ -O3 -Rpass=loop-vectorize -Rpass-analysis=loop-vectorize -std=c++11 -ffast-math test.cpp

test.cpp:2:5: remark: vectorized loop (vectorization width: 2, interleaved count: 2) [-Rpass=loop-vectorize]
    for(int i=0; i < Length; i++) v[i] += 1;
    ^

但是,非常相似的东西(仅在 call_add1 中移动参数)不起作用:

inline void add1(double *v, int Length) {
    for(int i=0; i < Length; i++) v[i] += 1;
}

void call_add1() {
    double v[20]={0,1,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9}; 
    int L=20;
    add1(v, L);
}

int main(){ return 0;}

使用相同的命令编译不会产生任何输出。为什么会这样?如何确保内联函数中的循环始终自动矢量化?我想矢量化许多函数循环,所以我希望修复不会太复杂。

4

3 回答 3

8

编译你的代码-fsave-optimization-record显示循环被展开然后被消除。

--- !Passed
Pass:            loop-unroll
Name:            FullyUnrolled
DebugLoc:        { File: main.cpp, Line: 2, Column: 5 }
Function:        _Z9call_add1v
Args:            
  - String:          'completely unrolled loop with '
  - UnrollCount:     '20'
  - String:          ' iterations'
...
--- !Passed
Pass:            gvn
Name:            LoadElim
DebugLoc:        { File: main.cpp, Line: 2, Column: 40 }
Function:        _Z9call_add1v
Args:            
  - String:          'load of type '
  - Type:            double
  - String:          ' eliminated'
  - String:          ' in favor of '
  - InfavorOfValue:  '0.000000e+00'

如果将 4000 个元素放入数组,它将超过优化器阈值,并且 clang 将启用矢量化。

于 2018-01-15T17:18:51.520 回答
4

那是因为对于第二种情况,编译器知道没有副作用,并优化了https://godbolt.org/g/CnojEi clang 4.0.0 中的所有内容,-O3仅使用叶子:

call_add1():
  rep ret
main:
  xor eax, eax
  ret

而且您没有关于循环魔术的营销。

在第一种情况下,编译器确实为函数生成了一些主体,因为函数确实修改了参数。如果您将其编译为目标文件。你可以链接到这个函数,它会起作用。我猜如果参数是 const,那么函数可能也会留下空的主体。

当您打印出内容时,程序并不相同,但它们都使用矢量化指令:https ://godbolt.org/g/KF1kNt

于 2018-01-15T17:14:47.693 回答
3

v当明确指定时,编译器似乎会简单地展开和优化循环。这是一件好事:不必执行的代码是最快的。

要验证它是一种优化,您可以尝试使一些变量易失(现场示例)。

于 2018-01-15T17:14:54.993 回答