我正在优化简单的遗传算法和神经网络,并且在GCC中摆弄一些选项以生成更快的可执行文件。
在我的代码中,我有一些断言,例如
mat mat_add(mat a, mat b)
{
assert(a->rows == b->rows);
assert(a->cols == b->cols);
mat m = mat_create(a->rows, a->cols);
for(size_t i = 0; i < a->rows; i++) {
for(size_t j = 0; j < a->cols; j++)
mat_set(m, i, j, mat_get(a, i, j) + mat_get(b, i, j));
}
return m;
}
我发现,如果添加-DNDEBUG
来禁用断言,则可执行文件会更快,因为它不会检查上述条件。但是,它实际上要慢一些。
没有-DNDEBUG
:
$ gcc src/*.c -lm -pthread -Iinclude/ -Wall -Ofast
$ for i in $(seq 1 5); do time ./a.out; done
real 0m11.677s
user 1m28.786s
sys 0m0.729s
real 0m11.716s
user 1m29.304s
sys 0m0.723s
real 0m12.217s
user 1m31.707s
sys 0m0.806s
real 0m12.602s
user 1m32.863s
sys 0m0.726s
real 0m12.225s
user 1m30.915s
sys 0m0.736s
使用-DNDEBUG
:
$ gcc src/*.c -lm -pthread -Iinclude/ -Wall -Ofast -DNDEBUG
$ for i in $(seq 1 5); do time ./a.out; done
real 0m13.698s
user 1m42.533s
sys 0m0.792s
real 0m13.764s
user 1m43.337s
sys 0m0.709s
real 0m13.655s
user 1m42.986s
sys 0m0.739s
real 0m13.836s
user 1m43.138s
sys 0m0.719s
real 0m14.072s
user 1m43.879s
sys 0m0.712s
速度并不慢,但是很明显。
是什么原因导致这种速度下降?
答案 0 :(得分:2)
mat_set
和mat_get
函数是否对索引执行自己的边界检查?在存在断言的情况下,仅当b->rows == a->rows
为true时,循环才可到达。这样一来,编译器可以优化i < b->rows
中mat_get
的所有检查b
,因为它通过循环条件知道b->rows == a->rows
和i < a->rows
。
如果最终还是这样,则可以通过添加(GNU C功能)来实现相同的功能而无需声明,也无需任何运行时分支:
if (a->rows != b->rows || a->cols != b->cols)
__builtin_unreachable();
一种更可移植但更不可靠的方法是在1/0;
正文中编写一些荒谬的未定义行为,例如if
。