如何优化共享库的大小?

时间:2011-11-05 16:21:40

标签: c++ optimization android-ndk shared-libraries

假设我们拥有包含许多不需要的功能的巨大静态库(在下面的示例中,我们有库lib1.alib2.a,其中包含不需要的函数g1()f2())。< / p>

我们希望使用一些导出的方法构建共享库,这些方法只使用那些巨大的库中的一些函数/类。请参阅下面的示例:我们要导出函数foo()

问题

  1. 我们可以告诉链接器(ld)我们要导出哪些函数/方法(就像我们在Windows中为DLL做的那样)?
  2. 链接器可以解析依赖并删除不需要的函数/方法吗?或者还有其他方法可以解决问题吗?
  3. 如果您有解决方案,请写下以下示例的修复程序。
  4. 示例

    档案1.h

    int f1( int n );
    int g1( int n );
    

    档案2.h

    int f2( int n );
    

    档案foo.cpp

    #include "1.h"
    #include "2.h"
    
    int foo( int n )
    {
        return f1( n );
    }
    

    档案1.cpp

    int f1( int n ) { return n; }
    int g1( int n ) { return n; }
    

    档案2.cpp

    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
    

    制作目标all后,我们有nm res/libFoo.so

    ...
    000001d8 T _Z2f1i
    0000020e T _Z2g1i
    000001c4 T _Z3fooi
    ...
    

    因此ld已根据目标文件之间的依赖关系删除了2.o目标文件。但是没有从g1()删除功能1.o

3 个答案:

答案 0 :(得分:4)

或许链接时间优化(即GCC 4.6的-flto选项)可能会有所帮助吗?

还有function attribute __attribute__ ((visibility ("hidden")))和/或__attribute__ ((weak))

进入*.so共享对象的代码应该使用-fPIC

进行编译

答案 1 :(得分:3)

我认为在-fPIC中不使用*.so会使它们包含由ld.so处理的大量重定位指令,因此这是首先尝试的内容。编译和链接时都应使用-flto,并增加编译时间。添加属性应该逐个函数完成,并且需要花费大量开发人员的时间(因为您需要选择需要它们的函数)。如果代码非常大(例如,超过100KLOC的源代码),您可以考虑编写GCC插件或最好是GCC MELT扩展来自定义GCC 4.6编译器以自动执行此类任务,但这需要一些工作(几周)而不是几小时)。

我是GCC MELT的主要作者(如果它对你有所帮助,我甚至会说一些不好的俄语),所以我很乐意帮助你使用MELT。但在您的情况下,只有当您的库足够大以证明使用MELT定制GCC的工作超过一周时才值得。

答案 2 :(得分:3)

首先,正如Basile指出的那样,您应该在构建-fPIC时添加lib{1,2}.a标记。

其次,您将所有1.o链接在一起,因为它是how UNIX linkers work

最简单的解决方案(比使用-flto简单得多)是通过将-Wl,--gc-sections添加到libFoo.so链接行来启用链接器垃圾回收,并使用{构建lib{1,2}.a {1}}。这将有效地将每个功能转变为自己独立的“书”。