Fin*_*ent 8 c optimization inline icc
我有一个关于C编译器优化的问题以及内联函数何时/如何循环展开.
我正在开发一个类似于下面例子的数字代码.基本上,my_for()
会计算某种模板并调用每个op()
数据.在这里,包装,创建参数并将函数指针发送给...谁的工作是修改每个()双数组的th double .my_type *arg
i
my_func()
my_for()
my_op()
i
arg->n
arg->dest[j]
typedef struct my_type {
int const n;
double *dest[16];
double const *src[16];
} my_type;
static inline void my_for( void (*op)(my_type *,int), my_type *arg, int N ) {
int i;
for( i=0; i<N; ++i )
op( arg, i );
}
static inline void my_op( my_type *arg, int i ) {
int j;
int const n = arg->n;
for( j=0; j<n; ++j )
arg->dest[j][i] += arg->src[j][i];
}
void my_func( double *dest0, double *dest1, double const *src0, double const *src1, int N ) {
my_type Arg = {
.n = 2,
.dest = { dest0, dest1 },
.src = { src0, src1 }
};
my_for( &my_op, &Arg, N );
}
Run Code Online (Sandbox Code Playgroud)
这很好用.函数按照它们应该内联,并且代码(几乎)与在单个函数中内联编写所有内容并展开j
循环一样高效,没有任何类型my_type Arg
.
这就是混乱:如果我设置int const n = 2;
而不是int const n = arg->n;
in my_op()
,那么代码就会像展开的单功能版本一样快.所以,问题是:为什么?如果所有内容都被内联到my_func()
,为什么编译器看不到我在字面上定义Arg.n = 2
?此外,当我明确地在j
循环上进行绑定时,没有任何改进arg->n
,这应该看起来就像int const n = 2;
内联后更快.我也尝试过my_type const
在任何地方使用它来向编译器发送这个常量信号,但它只是不想展开循环.
在我的数字代码中,这相当于大约15%的性能损失.如果重要,那么,n=4
这些j
循环出现在一个条件分支中op()
.
我正在编译icc(ICC)12.1.5 20120612.我试过了#pragma unroll
.这是我的编译器选项(我错过了任何好的吗?):
-O3 -ipo -static -unroll-aggressive -fp-model precise -fp-model source -openmp -std=gnu99 -Wall -Wextra -Wno-unused -Winline -pedantic
谢谢!
嗯,显然编译器不够“智能”,无法传播常量n
并展开for
循环。实际上它很安全,因为arg->n
可以在实例化和使用之间进行更改。
为了在各代编译器之间获得一致的性能并最大限度地利用代码,请手动展开。
像我这样的人在这些情况下(性能为王)所做的就是依赖宏。
宏将在调试版本中“内联”(有用),并且可以使用宏参数进行模板化(到某个点)。作为编译时常量的宏参数保证保持这种方式。
归档时间: |
|
查看次数: |
554 次 |
最近记录: |