我有一个很大的Swig Python模块。C++包装器大约有320,000行代码(包括头文件)。目前我使用-O1编译,g++生成的二进制文件大小为44MiB,编译需要约3分钟。
如果我关闭优化(-O0),那么二进制文件大小为40MiB,编译需要44秒。
使用-O0编译包装器是否会显著影响Python模块的性能?在我分析不同优化级别下模块性能之前,是否有人做过这种分析或者有任何见解呢?
我有一个很大的Swig Python模块。C++包装器大约有320,000行代码(包括头文件)。目前我使用-O1编译,g++生成的二进制文件大小为44MiB,编译需要约3分钟。
如果我关闭优化(-O0),那么二进制文件大小为40MiB,编译需要44秒。
使用-O0编译包装器是否会显著影响Python模块的性能?在我分析不同优化级别下模块性能之前,是否有人做过这种分析或者有任何见解呢?
无论是否使用SWIG模块,这都是不好的。即使使用gcc -O1
编译器,也会进行许多优化,如果您阻止它们发生,将会错过很多。
您可以通过检查所选编译器生成的汇编代码来检查差异。其中一些我知道很容易对SWIG生成的包装器造成负面影响:
Dead code elimination:
void foo() {
int a = 1;
a = 0;
}
With -O1 this entirely pointless code gets totally removed:
foo:
pushl %ebp
movl %esp, %ebp
popl %ebp
ret
whereas with -O0 it becomes:
foo:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $1, -4(%ebp)
movl $0, -4(%ebp)
leave
ret
Register allocation will be detrimentally impacted in functions with lots of local variables - most SWIG wrapper functions will see a hit from this. It's hard to show a concise example of this though.
Another example, the output from gcc compiling the SWIG wrapper for the prototype:
int foo(unsigned int a, unsigned int b, unsigned int c, unsigned int d);
Generates with -O0
:
Java_testJNI_foo:
pushl %ebp
movl %esp, %ebp
subl $88, %esp
movl 16(%ebp), %eax
movl %eax, -48(%ebp)
movl 20(%ebp), %eax
movl %eax, -44(%ebp)
movl 24(%ebp), %eax
movl %eax, -56(%ebp)
movl 28(%ebp), %eax
movl %eax, -52(%ebp)
movl 32(%ebp), %eax
movl %eax, -64(%ebp)
movl 36(%ebp), %eax
movl %eax, -60(%ebp)
movl 40(%ebp), %eax
movl %eax, -72(%ebp)
movl 44(%ebp), %eax
movl %eax, -68(%ebp)
movl $0, -32(%ebp)
movl -48(%ebp), %eax
movl %eax, -28(%ebp)
movl -56(%ebp), %eax
movl %eax, -24(%ebp)
movl -64(%ebp), %eax
movl %eax, -20(%ebp)
movl -72(%ebp), %eax
movl %eax, -16(%ebp)
movl -16(%ebp), %eax
movl %eax, 12(%esp)
movl -20(%ebp), %eax
movl %eax, 8(%esp)
movl -24(%ebp), %eax
movl %eax, 4(%esp)
movl -28(%ebp), %eax
movl %eax, (%esp)
call foo
movl %eax, -12(%ebp)
movl -12(%ebp), %eax
movl %eax, -32(%ebp)
movl -32(%ebp), %eax
leave
ret
Compared to -O1
which generates just:
Java_testJNI_foo:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl 40(%ebp), %eax
movl %eax, 12(%esp)
movl 32(%ebp), %eax
movl %eax, 8(%esp)
movl 24(%ebp), %eax
movl %eax, 4(%esp)
movl 16(%ebp), %eax
movl %eax, (%esp)
call foo
leave
ret
With -O1
g++ can generate far smarter code for:
%module test
%{
int value() { return 100; }
%}
%feature("compactdefaultargs") foo;
%inline %{
int foo(int a=value(), int b=value(), int c=value()) {
return 0;
}
%}