过去的手动优化(C 语言)



早在 70 年代 C 刚开始时,我想编译器级优化并不像现代编译器(clang、gcc 等(那样先进,而且计算机本身在硬件方面受到限制,是否通常更喜欢源代码级别的优化而不是可读性?

例:

int arrayOfItems[30]; // Global variable
int GetItemAt(int index)
{
    return globalArrayOfThings[index];
}
int main()
{
    // Code
    // ... arrayOfItems intialized somewhere
    // More code
    GetSomethingByItem(GetItemAt(4)); // Get at index 4
    return 0;
}

现在可以优化为:

int arrayOfItems[30]; // Global variable
int main()
{
    // Code
    // ... arrayOfItems intialized somewhere
    // More code
    GetSomethingByItem(arrayOfItems[4]); // Get at index 4
    return 0;
}

完全省略函数GetItemAt,从而通过直接从其地址访问值而不是输入函数、创建堆栈帧、访问值并将结果推送到某个寄存器来节省时间。人们过去是否更喜欢将第二个"优化"版本直接写入源代码,或者使用第一个版本,以便代码更具可读性?

我知道在这个例子中,你可以使用处理器来"模仿"这种优化(例如#define GetItemAt(x) arrayOfItems[x](,但你明白我的意思。

另外,也许从一开始就存在这种确切的优化功能,如果是这样,我应该找到另一个示例,欢迎提出建议。

博士 -

  • 过去是否普遍喜欢源代码级别的优化而不是可读性?

奖金问题:

    是否有仅为了源代码更具
  • 可读性而包含的优化?

我认为没有多少开发人员更喜欢优化而不是可读性,但有时可能会争辩说,有些优化会损害可读性,但对于性能是必要的。类似于达夫的设备(循环展开优化(

do {               /* count > 0 assumed */
  *to = *from++;   /* "to" pointer is NOT incremented, see explanation below */
} while(--count > 0);

register n = (count + 7) / 8;
switch(count % 8) {
case 0: do {    *to = *from++;
case 7:     *to = *from++;
case 6:     *to = *from++;
case 5:     *to = *from++;
case 4:     *to = *from++;
case 3:     *to = *from++;
case 2:     *to = *from++;
case 1:     *to = *from++;
    } while(--n > 0);
}

当然,事实证明编译器变得更聪明了,LKML上已经报道了删除Duff的设备提高了性能并减少了内存使用。来自链接的维基百科,

出于内存到内存副本的目的(这不是 Daff 设备的原始用途,尽管可以修改以达到此目的,如下一节所述(,标准 C 库提供了函数memcpy;它的性能不会比此代码的内存到内存复制版本差,并且可能包含特定于体系结构的优化,这将使它明显更快

和来自LKML(2000年(

。这种效果在 X 服务器中。 事实证明,通过分支预测和CPU的相对速度 与过去十年的内存变化相比,循环展开几乎是 毫无 意义。事实上,通过消除达夫设备的所有实例 XFree86 4.0服务器,服务器的大小缩小了半兆字节,启动速度更快,因为消除了所有 多余的代码意味着X服务器没有破坏缓存。 行一样多。

至于只提高可读性的优化,它要求你的代码首先是不可读的。那么任何使其更具可读性的东西似乎都符合条件。最后,请记住,过早优化是万恶之源。

最新更新