带-Ofast的-DNDEBUG比仅-Ofast慢

时间:2018-12-21 01:32:37

标签: c gcc optimization

我正在优化简单的遗传算法和神经网络,并且在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

速度并不慢,但是很明显。

是什么原因导致这种速度下降?

1 个答案:

答案 0 :(得分:2)

mat_setmat_get函数是否对索引执行自己的边界检查?在存在断言的情况下,仅当b->rows == a->rows为true时,循环才可到达。这样一来,编译器可以优化i < b->rowsmat_get的所有检查b,因为它通过循环条件知道b->rows == a->rowsi < a->rows

如果最终还是这样,则可以通过添加(GNU C功能)来实现相同的功能而无需声明,也无需任何运行时分支:

if (a->rows != b->rows || a->cols != b->cols)
    __builtin_unreachable();

一种更可移植但更不可靠的方法是在1/0;正文中编写一些荒谬的未定义行为,例如if