【问题标题】:Eliminating redundant loads of GOT register?消除 GOT 寄存器的冗余负载?
【发布时间】:2011-07-01 19:16:56
【问题描述】:

我正在处理一些在编译为 PIC(位置无关代码)时会变慢 70-80% 的代码,并正在寻找缓解问题的方法。问题的很大一部分是 gcc 坚持在每个函数中插入以下内容:

call __i686.get_pc_thunk.bx
addl $_GLOBAL_OFFSET_TABLE_,%ebx

即使这最终是函数内容的 20%。现在,ebx 是一个保留调用的寄存器,相关翻译单元(源文件)中的每个 函数都在将 GOT 的地址加载到其中,很容易检测到 static不能从翻译单元外部调用函数(它们的地址永远不会被占用)。那么为什么 gcc 不能在大外部链接函数的开头加载一次ebx,并生成静态链接函数,以便它们假设ebx 已经加载了 GOT 的地址?是否有任何优化标志我可以用来强制 gcc 进行这种明显且大规模的优化,而不是将内联限制提高到天价,以便所有内容都内联到外部函数中?

【问题讨论】:

  • 这看起来相关且令人失望:gcc.gnu.org/bugzilla/show_bug.cgi?id=23756
  • 仅仅因为你的函数是静态的并不意味着它们不能作为函数指针传递给其他编译单元。我想知道添加 GOT 表是否真的会大大减慢这些功能。你真的介绍过它吗?如果是这样,那么这样短的函数是内联的好候选,这也节省了创建堆栈帧的开销。
  • @datenwolf:正如我在问题中所说,它们的地址永远不会被占用,因此它们不能作为函数指针传递。 gcc 已经对永远不能被外部调用的函数进行了一些优化(比如切换到寄存器传递)。

标签: c optimization gcc pic


【解决方案1】:

可能没有通用的解决方法,但您可以尝试使用内联选项。我猜想编译单元中的static 函数没有太多的调用者,所以代码复制的开销不会太糟糕。

使用 gcc 强制执行此类操作的最简单方法是设置 attribute((always_inline))。您可以使用依赖于 gcc 的宏来确保可移植性。

如果您不想修改代码(但无论如何static inline 会很好),您可以使用-finline-limit 选项对其进行微调。

【讨论】:

  • -O3 以文件大小翻倍为代价解决了问题。由于缓存问题,这是否会损害某些调用者的性能,目前还没有任何消息......
  • 嗯,我假设你已经开启了一定程度的-O。如果您对 -O3 所做的不满意,您可以挑选其中的一些选项。
  • 我通常使用-Os,它倾向于生成更小的代码,并且性能大致相同(有时比-O3快10%,有时慢10%)。
  • @R..,所以编译器按照你的要求做了,优化大小而不是速度。
  • 勾选为已接受。这似乎是目前唯一的解决方法。 gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html visibility("internal") 的文档,它允许 gcc 优化加载 PIC 寄存器,但 visibilitystatic 函数无效,这些函数(只要它们的地址未被占用)应该已经在一个类中反正和internal一样严格。所以它似乎只是一个 gcc 错误/限制。 :-(
【解决方案2】:

不是一个真正的解决方案,但是:如果所讨论的函数不引用文件范围变量,您可以将它们全部放在一个翻译单元中并在没有 -fPIC 标志的情况下编译它。然后像往常一样将它们与最终 SO 中的其他文件链接在一起。

【讨论】:

  • 如果他们不访问静态存储持续时间的变量,GOT 寄存器一开始就不会被加载。问题是他们这样做了,而且必须这样做。
  • 如何将所有全局变量打包到一个结构中并将指向该结构的指针作为函数参数传递?则不会计算 GOT。
  • 添加 +1 - 这实际上是一个有用的想法,但不太适用于我的问题,因为该函数同时处理外部和静态全局状态(前者以只读方式,后者读写),当我更改所有这些小函数以向它们传递两个额外的指针时,我认为我添加的开销将与 PIC 寄存器加载一样糟糕。
猜你喜欢
  • 2011-03-13
  • 2020-09-29
  • 1970-01-01
  • 1970-01-01
  • 2014-03-26
  • 1970-01-01
  • 1970-01-01
  • 2016-07-25
  • 1970-01-01
相关资源
最近更新 更多