【问题标题】:C coding practices for performance or code size - beyond what a compiler does性能或代码大小的 C 编码实践 - 超出编译器的功能
【发布时间】:2010-11-10 22:41:27
【问题描述】:

我想看看程序员可以用 C 做什么,这可以决定生成的目标文件的性能和/或大小。

例如,
1. 将简单的 get/set 函数声明为内联可能会提高性能(以更大的占用空间为代价)
2.对于不使用循环变量本身的值的循环,倒数到零而不是倒数到某个值 等等

看起来编译器现在已经发展到根本不需要“简单”技巧(如上述两点)的水平。 Appropriate options during compilation do the job anyway.哎呀,我还在这里看到了关于编译器如何处理递归的帖子——这非常有趣!那么我们在 C 级别还剩下什么要做呢? :)

我的具体环境是:GCC 4.3.3 re-targeted for ARM architecture (v4)。但是也欢迎对其他编译器/处理器的响应,并将被咀嚼。

PS:我的这种方法违背了通常的“代码优先!然后是基准测试,然后最后优化”的方法。

编辑:恰巧,我在发完问题后发现了一个类似的帖子:Should we still be optimizing "in the small"?

【问题讨论】:

标签: c optimization gcc micro-optimization


【解决方案1】:

许多人没有意识到这一点:定义一个内联标签(因编译器而异),这意味着内联,在其意图中 - 许多编译器将关键字置于与原始含义完全不同的上下文中。还有一些方法可以在编译器开始将琐碎的事情弹出之前增加内联大小限制。人工内联可以生成更快的代码(编译器通常比较保守,或者没有考虑足够多的程序),但是您需要学习正确使用它,因为它可能(很容易)适得其反。是的,这绝对适用于代码大小和速度。

【讨论】:

    【解决方案2】:

    补充马丁上面所说的缓存友好性:

    • 对结构进行重新排序,以使通常一起访问的字段位于同一缓存行中会有所帮助(例如,仅加载一个缓存行而不是两个缓存行。)您实际上是在增加您的有用数据的密度通过这样做来缓存数据。有一个 linux 工具可以帮助你做到这一点:dwarves 1http://www.linuxinsight.com/files/ols2007/melo-reprint.pdf

    • 您可以使用类似的策略来增加代码的密度。在 gcc 中,您可以使用可能/不太可能的标签标记热分支和冷分支。这使 gcc 能够单独保留冷分支,这有助于增加 icache 密度。

    现在来点完全不同的:

    • 对于可能跨 CPU 访问(读取)的字段,相反的策略是有意义的。问题在于,出于一致性目的,只能允许一个 CPU 写入相同的地址(实际上是相同的高速缓存行)。这可能导致称为高速缓存行乒乓的情况。这很糟糕,如果该缓存行包含其他不相关的数据,情况可能会更糟。在这里,将这些竞争数据填充到缓存行长度是有意义的。

    注意:这些显然是微优化,仅在您试图从代码中提取最后一点性能时才进行。

    【讨论】:

      【解决方案3】:

      如今的编译器仍然不太擅长向量化您的代码,因此您仍然希望自己对大多数算法进行 SIMD 实现。

      为您的确切问题选择正确的数据结构可以显着提高性能(在这种特定情况下,我见过从 Kd-tree 迁移到 BVH 的情况)。

      编译器可能会填充一些结构/变量以适应缓存,但其他缓存优化(例如数据的局部性)仍取决于您。

      编译器仍然不会自动使您的代码成为多线程的,并且根据我的经验,使用 openmp 并没有太大帮助。 (无论如何,您确实必须了解 openmp 才能显着提高性能)。所以目前,你自己在做多线程。

      【讨论】:

        【解决方案4】:

        在可能的情况下进行预计算...(抱歉,但这并不总是可能的...我在国际象棋引擎上进行了广泛的预计算。)将这些结果存储在内存中,记住缓存......内存中预计算数据的大小越大进行缓存命中的机会越小。由于最近的大多数硬件都是多核的,因此您可以针对它设计应用程序。

        如果您使用多个大数组,请确保将它们分组到彼此靠近的位置,以提高缓存命中率

        【讨论】:

          【解决方案5】:

          “始终”知道算法的时间和空间复杂度。编译器将永远无法像您一样完成这项工作。 :)

          【讨论】:

            【解决方案6】:

            我能想到的编译器可能不会优化的一件事是“缓存友好性”:例如,如果您以行优先顺序迭代二维数组,请确保您的内部循环运行列索引以避免缓存抖动。让内部循环在错误的索引上运行可能会导致巨大的性能损失。

            这适用于所有编程语言,但如果您使用 C 进行编程,性能可能对您来说至关重要,因此尤为重要。

            【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-01-05
            • 2011-03-27
            • 1970-01-01
            • 1970-01-01
            • 2014-11-28
            • 2021-04-19
            • 1970-01-01
            相关资源
            最近更新 更多