最好不要相信 gcc 默认内联器?
Better not trust the gcc default inliner?
我正在做一些手工优化我的一些代码并以某种方式被 gcc 咬伤。
原代码在运行通过测试时大约需要3.5秒执行完毕
我很困惑为什么我的优化版本现在需要大约 4.3 秒才能完成测试?
我将 __attribute__((always_inline))
应用于探查器中突出的局部静态函数之一,现在它在 2.9 秒内自豪地 运行s。不错
我一直相信 gcc 可以在函数内联中做出决定,但显然它似乎并不那么完美。我不明白为什么 gcc 最终会做出一个非常错误的决定,是否要使用 -O3 -flto -fwhole-program
内联文件范围的静态函数。编译器真的只是在猜测内联函数的成本效益吗?
编辑:要回答实际问题,是的,编译器确实 "guesstimate" - 或者作为技术术语,它使用 "heuristics" - 确定速度增益与 space 内联特定函数将导致。启发式定义为 "a practical but not theoretically perfect solution"。结束编辑。
如果没有看到代码,很难说出编译器中发生了什么。您正在做正确的事情来分析您的代码,尝试您的手动优化并再次分析 - 如果更好,请保留它!
编译器时常出错的情况并不少见。人类有时更聪明——但我通常相信编译器会把它做好。可能是该函数被调用了很多次并且相当大,因此编译器决定 "it's above the threshold for code-bloat vs. speed gain"?或者它可能只是没有正确计算 "how much better/worse is it to inline" 。
请记住,编译器是通用的,适用于一种情况的方法可能会使另一种情况变得更糟 - 因此编译器必须妥协并提出一些合理的启发式方法,这些启发式方法不会经常给出太糟糕的结果.
如果您可以运行 profile-guided 优化,它可能会帮助编译器做出正确的决定(因为它会知道有多少次迭代以及采用特定分支的频率)...
如果您可以与 GCC 编译器团队共享代码,请将其作为错误报告 - 他们可能 ignore/reject 将其作为 "too special" 或类似的,但这种特殊情况很可能是某事 "that got missed out".
我认为说编译器 "gets it right more often than not" 是公平的,但这并不意味着它总是正确的。我最近看了一些从 Clang 生成的代码,它有一大堆额外的指令来展开循环——但在最典型的情况下,循环将是一次迭代,而且永远不会超过 16 次。所以展开循环的额外指令对于 1 的情况,4 倍的循环完全浪费了,即使对于可能的最长循环也是相当无用的。自然循环 "rolled" 大约只有 3-4 条指令,因此即使循环大得多,节省的空间也很小——当然,如果是一百万次迭代,速度可能会提高三倍该功能的。
我正在做一些手工优化我的一些代码并以某种方式被 gcc 咬伤。
原代码在运行通过测试时大约需要3.5秒执行完毕
我很困惑为什么我的优化版本现在需要大约 4.3 秒才能完成测试?
我将 __attribute__((always_inline))
应用于探查器中突出的局部静态函数之一,现在它在 2.9 秒内自豪地 运行s。不错
我一直相信 gcc 可以在函数内联中做出决定,但显然它似乎并不那么完美。我不明白为什么 gcc 最终会做出一个非常错误的决定,是否要使用 -O3 -flto -fwhole-program
内联文件范围的静态函数。编译器真的只是在猜测内联函数的成本效益吗?
编辑:要回答实际问题,是的,编译器确实 "guesstimate" - 或者作为技术术语,它使用 "heuristics" - 确定速度增益与 space 内联特定函数将导致。启发式定义为 "a practical but not theoretically perfect solution"。结束编辑。
如果没有看到代码,很难说出编译器中发生了什么。您正在做正确的事情来分析您的代码,尝试您的手动优化并再次分析 - 如果更好,请保留它!
编译器时常出错的情况并不少见。人类有时更聪明——但我通常相信编译器会把它做好。可能是该函数被调用了很多次并且相当大,因此编译器决定 "it's above the threshold for code-bloat vs. speed gain"?或者它可能只是没有正确计算 "how much better/worse is it to inline" 。
请记住,编译器是通用的,适用于一种情况的方法可能会使另一种情况变得更糟 - 因此编译器必须妥协并提出一些合理的启发式方法,这些启发式方法不会经常给出太糟糕的结果.
如果您可以运行 profile-guided 优化,它可能会帮助编译器做出正确的决定(因为它会知道有多少次迭代以及采用特定分支的频率)...
如果您可以与 GCC 编译器团队共享代码,请将其作为错误报告 - 他们可能 ignore/reject 将其作为 "too special" 或类似的,但这种特殊情况很可能是某事 "that got missed out".
我认为说编译器 "gets it right more often than not" 是公平的,但这并不意味着它总是正确的。我最近看了一些从 Clang 生成的代码,它有一大堆额外的指令来展开循环——但在最典型的情况下,循环将是一次迭代,而且永远不会超过 16 次。所以展开循环的额外指令对于 1 的情况,4 倍的循环完全浪费了,即使对于可能的最长循环也是相当无用的。自然循环 "rolled" 大约只有 3-4 条指令,因此即使循环大得多,节省的空间也很小——当然,如果是一百万次迭代,速度可能会提高三倍该功能的。