【问题标题】:How to keep calls inlined but avoid compiler warnings?如何保持调用内联但避免编译器警告?
【发布时间】:2014-01-23 10:05:40
【问题描述】:

我有一个类,它在我的软件中的所有代码中广泛使用。这个类是带有一些几何函数的点抽象,它是由坐标类型模板化的,例如 long 或 double。如果这一点在动态库之间传递,我会收到编译器警告C4252:类“Point”需要有 dll 接口供“OtherClass”类的客户端使用,但代码运行良好且快速,因为所有库具有相同的编译设置。

由于我们的政策是完全没有警告,我们决定将两个广泛使用的模板特化标记为 dll 导出,如下所示(在带有模板的标题中):

 template class __declspec(dllexport) Point<long>;
 template class __declspec(dllexport) Point<double>;

但现在编译器将所有内联替换为常用函数调用,并且由于此类被大量使用,我们的代码变得慢得多。

问题是:有没有办法在每个使用它的库中保持内联调用,但仍然在 dll 之间传递数据并避免警告?

我们使用 Microsoft VC++ 2012 编译器。

【问题讨论】:

  • 根据我的经验,如果速度是一个问题,我通过使用静态链接代码而不是 DLL 获得了更好的性能,这正是您所描述的原因,但是,回想起来,我认为这可能是实现的某种方式您在说什么,尽管我怀疑基准测试仍会显示静态链接的代码更快。我正在研究强制 dll 在加载时驻留在内存中,以便可以实现与内置/静态链接代码类似的性能。我会在今天晚些时候回复你:)
  • 问题不在于将调用从静态更改为动态,而在于将内联函数更改为常用调用函数。一旦您标记了您的类/函数 dll 导出,您将不再获得任何内联函数。
  • 是的,这又回到了静态/动态链接:如果它在 DLL 中,你不能在编译时 inline 代码,因为编译器不知道函数背后代码的细节。源代码必须在编译时可用以内联到调用位置,此时 dll 变得不必要,因为代码已经在 exe 中。
  • 我知道 DLL 使代码更新更加直接,但是对于原始速度必须进行一些权衡:我有 exes 的重量超过 500MB 但是,它们是必需的尽可能快地运行的能力,当速度不是问题并且易于维护/内存占用时,DLL 几乎总能节省一天:)
  • 类源在编译阶段确实可用,但问题是它在其他DLL之间传递。是的,它有效,但会发出警告。所以我们对当前架构没问题,但我正在考虑警告消失。

标签: c++ visual-c++ dll


【解决方案1】:

根据我的经验,如果速度是一个问题,我通过仅使用静态链接代码/标题 inlinefastcall 代码而不是 DLLs 获得了更好的性能,但回想起来,这正是您所描述的原因我认为可能有一些方法可以实现您所说的,尽管我怀疑基准测试仍然会显示静态链接的代码更快。

我正在考虑强制 DLLs 在加载时驻留在内存中(并且确实可以按照文章 here 执行此操作),以便与内联/静态类似的性能可以实现代码联动。

起初这似乎可行,但是如果在任何时候对加载DLL 的内存进行分页,它将变得与动态访问DLL 代码一样慢。

在我研究了链接到DLLs 的进一步方法之前,如果可以选择,我个人会推荐inlinefastcall 代码,尽管这会导致可执行文件变大,如果内存占用有问题,可能会影响您的发布。

作为您上面提到的两行的快速修复,您可以尝试更改:

template class __declspec(dllexport) Point<long>;
template class __declspec(dllexport) Point<double>;

extern template class __declspec(dllexport) Point<long>;
extern template class __declspec(dllexport) Point<double>;

这应该有望消除您遇到的问题。 (有关执行此操作的更多信息,请访问 here,在该页面上用于导出 STL 类或包含 STL 类的对象的实例,这似乎是相同的问题)。

然而,内联原则(编译阶段“优化”)天生就与从 DLL 导出/导入相反:前者通过将所有内容连接到一个包中来优化速度,后者通过以下方式优化内存占用/模块化将代码分成不同的更小的包,可以在需要时调用。

注意事项:

  1. fastcall 将一个或多个参数放入寄存器, 从而减少执行所需的内存调用次数。

    • 对于 MSoft 和 GCC,当您只有 2 个参数时,您将获得最显着的速度优势。
    • Borland 编译器最多允许使用 3 个寄存器/参数
  2. 还要注意,inline 关键字并不能保证代码会 内联,这只是对 编译器,但是其他优化标志可以删除它或内联 您未标记为内联的其他部分。

希望这会有所帮助,但与往常一样,如果您需要更多信息/帮助,请告诉我。 :)

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-08
  • 1970-01-01
  • 2011-10-26
  • 1970-01-01
  • 1970-01-01
  • 2019-02-21
  • 1970-01-01
相关资源
最近更新 更多