【问题标题】:-DNDEBUG with -Ofast is slower than only -Ofast-DNDEBUG 与 -Ofast 比仅 -Ofast 慢
【发布时间】:2018-12-21 01:32:37
【问题描述】:

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

速度并不慢,但很明显。

什么可能导致这种放缓?

【问题讨论】:

  • 我不知道答案,但您是否尝试创建 size_t arows = a-&gt;rowssize_t brows = b-&gt;rows 的本地副本。我已经看到这是一种可能的优化,因为循环不必一直通过指针间接检查值。 (毕竟,它们可以在其他线程中被更改。)也许断言正在帮助编译器创建这两个的有效本地副本。
  • @alfC 我试过你的建议,性能确实提高了大约 0.5 秒。我想知道为什么在使用断言时可以优化指针间接,因为在这些断言之后值仍然可以改变。
  • 这不能回答您的问题,但mat_getmat_set 的定义是否在同一个翻译单元中可见(例如,标题中的内联函数)?如果没有,你就是在浪费时间用优化标志来追逐微小的收益(并且冒着破坏事情的风险,因为-Ofast 执行无效的转换)并且应该使它们内联,或者更好的是,摆脱访问器函数并使用二维数组直接地。这可能会产生一个数量级的差异。
  • @Demindiro,我不是专家,但我认为-Ofast 可以进行非法律优化,而这本身就会使您陷入疯狂的境地。你用-O3测试了吗?也许到那时它会有意义,即使你的整体运行时间会变慢。
  • 检查生成的代码,你就会有答案。猜测是没有意义的

标签: c gcc optimization


【解决方案1】:

mat_setmat_get 函数是否对索引执行自己的边界检查?在存在断言的情况下,只有在 b-&gt;rows == a-&gt;rows 为真时才能访问循环。这允许编译器优化在mat_get 中对b 的任何检查i &lt; b-&gt;rows,因为它通过循环条件知道b-&gt;rows == a-&gt;rowsi &lt; a-&gt;rows

如果最终出现这种情况,您可以通过添加(GNU C 功能)在没有断言和任何运行时分支的情况下实现相同的效果:

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

一种更便携但不太可靠的方法是在if 正文中编写一些无意义的未定义行为,例如1/0;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-20
    • 2013-01-07
    • 2017-08-31
    • 1970-01-01
    • 2015-02-03
    • 1970-01-01
    • 2012-05-13
    • 2011-06-14
    相关资源
    最近更新 更多