C++ 如何优化共享库的大小?
假设我们有巨大的静态库,其中包含许多不需要的特性(在下面的示例中,我们有库C++ 如何优化共享库的大小?,c++,optimization,android-ndk,shared-libraries,C++,Optimization,Android Ndk,Shared Libraries,假设我们有巨大的静态库,其中包含许多不需要的特性(在下面的示例中,我们有库lib1.a和lib2.a,其中包含不需要的函数g1()和f2()) 我们希望用几个导出的方法构建共享库,这些方法只使用来自庞大库的几个函数/类。请参见下面的示例:我们希望导出函数foo() 问题 我们能告诉链接器(ld)我们想要导出哪些函数/方法吗(就像我们在Windows中为DLL所做的那样) 链接器能否解决依赖关系并删除不需要的函数/方法?或者有没有其他办法解决这个问题 如果您有解决方案,请为下面的示例编写修复程序
lib1.a
和lib2.a
,其中包含不需要的函数g1()
和f2()
)
我们希望用几个导出的方法构建共享库,这些方法只使用来自庞大库的几个函数/类。请参见下面的示例:我们希望导出函数foo()
问题
ld
)我们想要导出哪些函数/方法吗(就像我们在Windows中为DLL所做的那样)1.h
:
int f1( int n );
int g1( int n );
int f2( int n );
文件2.h
:
int f1( int n );
int g1( int n );
int f2( int n );
文件foo.cpp
:
#include "1.h"
#include "2.h"
int foo( int n )
{
return f1( n );
}
int f1( int n ) { return n; }
int g1( int n ) { return n; }
int f2( int n ) { return n; }
文件1.cpp
:
#include "1.h"
#include "2.h"
int foo( int n )
{
return f1( n );
}
int f1( int n ) { return n; }
int g1( int n ) { return n; }
int f2( int n ) { return n; }
文件2.cpp
:
#include "1.h"
#include "2.h"
int foo( int n )
{
return f1( n );
}
int f1( int n ) { return n; }
int g1( int n ) { return n; }
int f2( int n ) { return n; }
文件makefile
:
CXXFLAGS = -g -I. -Wall -Wno-sign-compare
all: prepare libFoo.so
clean:
rm -f obj/*.a obj/*.o res/*.so
prepare:
mkdir -p src
mkdir -p obj
mkdir -p res
lib1.a lib2.a: lib%.a: %.o
ar r obj/$@ obj/$*.o
1.o 2.o foo.o: %.o:
g++ $(CXXFLAGS) -c -o obj/$@ src/$*.cpp
libFoo.so: lib1.a lib2.a foo.o
ld -shared -o res/libFoo.so obj/foo.o -Lobj -l1 -l2
在制作了targetall
之后,我们有了nm res/libFoo。因此
:
...
000001d8 T _Z2f1i
0000020e T _Z2g1i
000001c4 T _Z3fooi
...
因此,
ld
根据对象文件之间的依赖关系删除了2.o
对象文件。但是没有从1.o
中删除函数g1()
,也许链接时间优化(即GCC 4.6的选项)会有所帮助
以及\uuuu属性(可见性(“隐藏”))
和/或\uuu属性(弱))
所以共享对象应该用
-fPIC
编译。我相信在*中不使用-fPIC
。所以会使它们包含大量由ld处理的重定位指令。所以这是第一件尝试的事情<编译和链接时都应使用code>-flto
,并增加编译时间。添加属性应该一个函数一个函数地完成,并且会占用开发人员大量的时间(因为您需要选择哪些函数需要它们)。如果代码真的很大(例如,超过100KLoC的源代码),您可以考虑编码GCC插件,或者最好是扩展来定制GCC 4.6编译器来自动化这些任务,但是这需要一些工作(周,而不是小时)。p>
我是GCC MELT的主要作者(如果对你有帮助的话,我甚至会说一些糟糕的俄语),所以我很乐意帮助你使用MELT。但在您的情况下,只有当您的库足够大,足以证明使用MELT定制GCC的工作时间超过一周时,才有价值。首先,正如Basile指出的,您应该在构建库{1,2}.a
时添加-fPIC
标志
第二,您可以链接所有1.o
,因为这是一个很好的例子
最简单的解决方案(比使用-flto
简单得多)是通过向libFoo.so
链接行添加-Wl,--gc节来启用链接器垃圾收集,并使用-ffunction节-fdata节构建lib{1,2}.a
。这将有效地将每个函数变成它自己单独的“书”。我认为在*中不使用-fPIC
。因此使它们包含大量由ld处理的重新定位指令。因此感谢-flto
提示。我认为应该行得通。小问题是我在windows下使用交叉编译器,它不知道-flto
选项。将立即在Mac上试用:)
-flto
仅适用于最新的GCC编译器(即GCC 4.6)。我在PC上找到的所有GCC发行版都是4.4.*及更旧版本。将生成4.6.2版本并重试。我已经尝试了g++$(cxflags)-f函数节-fdata节-c-o obj/$@src/$*.cpp
。但是g1()
仍然存在于libFoo.so
中。我错过了什么吗?感谢“UNIX链接器如何工作”链接。我在找这样的文章。是的:你还必须打开链接器垃圾收集(我更新了答案)。谢谢。它起作用了<代码>:)
。将阅读关于-ffunction sections-fdata sections
arguments.@artyom.stv您误解了GCC文档。这些选项使GCC生成更大的对象文件,但不会显著影响最终的可执行文件(或共享库)。这些选项还使最终链接变慢,但不会影响可执行文件(或共享库)本身的执行速度。-flto
很可能会给您带来额外的加速,因此您可能无论如何都应该对此进行研究。因为我们得到了。因此
库~8Mb。以及其他一些数据。一些安卓设备没有足够的内存来安装应用程序。好的,“安卓”。在这种情况下的魔术词。那么请联系我或其他人使用gcc-melt@googlegroups.com(文档确实不够充分!)