当使用许多SWIG生成的模块时,避免重复的SWIG样板

时间:2011-11-18 06:14:33

标签: optimization gcc lua swig boilerplate

使用SWIG生成接口模块时,生成的C / C ++文件包含大量静态样板函数。因此,如果想要通过在同一个应用程序中使用许多单独编译的小接口来模块化SWIG生成的接口的使用,那么由于这些重复的功能,最终会导致很多膨胀。

使用gcc的-ffunction-sections选项和GNU链接器的--icf=safe选项(-Wl,--icf=safe到编译器),可以删除一些重复,但绝不是全部(I认为它不会合并任何有重新定位的东西 - 许多这些函数都会这样做。)

我的问题:我想知道是否有办法删除更多这种重复的样板,理想情况是不依赖于GNU特定的编译器/链接器选项。

特别是,是否有SWIG选项/标志/某些内容表示“每个输出文件中不包含样板文件”?实际上一个SWIG选项,-external-runtime告诉它生成一个“仅限样板”的输出文件,但没有明显的方法来抑制每个普通输出文件中包含的副本。 [我认为在SWIG中实现这种事情应该相当简单,所以我很惊讶它似乎不存在......但我似乎无法找到任何记录。]

这是一个小例子:

给定模块swg-oink.swg的接口文件swt_oink

%module swt_oink
%{ extern int oinker (const char *x); %}
extern int oinker (const char *x);

...以及swg-barf.swg的类似界面swt_barf

%module swt_barf
%{ extern int barfer (const char *x); %}
extern int barfer (const char *x);

...和测试主文件swt-main.cc

extern "C"
{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

extern int luaopen_swt_oink (lua_State *);
extern int luaopen_swt_barf (lua_State *);
}

int main ()
{
  lua_State *L = lua_open();
  luaopen_swt_oink (L);
  luaopen_swt_barf (L);
}

int oinker (const char *) { return 7; }
int barfer (const char *) { return 2; }

并将它们编译为:

swig -lua -c++ swt-oink.swg
g++ -c -I/usr/include/lua5.1 swt-oink_wrap.cxx
swig -lua -c++ swt-barf.swg
g++ -c -I/usr/include/lua5.1 swt-barf_wrap.cxx
g++ -c -I/usr/include/lua5.1 swt-main.cc
g++ -o swt swt-main.o swt-oink_wrap.o swt-barf_wrap.o

然后每个 xxx _wrap.o文件的大小约为16KB,其中95%是样板文件,最终可执行文件的大小大约是这些文件的总和,约为39K。如果使用-ffunction-sections编译每个接口文件,并使用-Wl,--icf=safe进行链接,则最终可执行文件的大小为34KB,但仍然存在大量重复(在可执行文件上使用nm可以看到多次定义的大量函数,并查看它们的来源,很明显,对大多数函数使用单个全局定义会很好。)

2 个答案:

答案 0 :(得分:2)

我很确定SWIG没有这样做的选择。我现在正在推测,但我认为原因可能是对使用不同版本的SWIG构建的模块的可见性的关注。想象一下以下场景:

两个库X和Y都使用SWIG为其代码提供接口。他们都选择在不同的翻译单元中显示“SWIG胶水”内容,以减少代码大小。如果X和Y都使用相同版本的SWIG,那么这一切都会很好。如果X使用SWIG 1.1而Y使用SWIG 1.3会发生什么?这两个模块都可以自行运行,但是根据平台如何处理共享对象以及语言本身如何加载它们(RTLD_GLOBAL?),一些可能非常糟糕的事情会发生在组合使用的两个模块中。相同的VM。

我怀疑代码重复的代价非常低 - 虚拟机和本机代码之间的交换成本通常非常高,这可能使稍微减少的指令缓存命中率相形见绌,尽管看到真正的基准测试可能会很有趣。从好的方面来说,这是用户无需担心的代码,因为它全部是自动生成的,并且都是为相应版本编写的接口正确保存的。

答案 1 :(得分:1)

我可能会有点迟到,但这是一个解决方法:

  • 在SWIG(< = 1.3)中有-noruntime命令行选项
  • 由于SWIG 2.0 -noruntime已被弃用,所以现在应该将-DSWIG_NOINCLUDE传递给C预处理器 - 而不是swig本身

我完全不确定这是否正确,但它至少对我有用。我将在SWIG的邮件列表中澄清这个问题。