gcc:去除未使用的功能 [英] gcc: Strip unused functions

查看:202
本文介绍了gcc:去除未使用的功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注意到有时即使我没有使用 iostream 和相关的I / O库,Mingw生成的二进制文件仍然不合理地大。

b
$ b

例如,我编写了一个代码来仅使用 vector cstdio ,用 -O2 -flto 编译它,我的程序可以大到2MB!我运行 nm main.exe> e.txt ,并且震惊地看到里面的所有 iostream 相关函数。



在Google搜索之后,我学会了使用 -ffunction-sections -Wl,-gc-sections ,将程序大小从2MB减少到〜300KB(如果使用 -s ,100 + KB)。很好!

为了进一步测试 -ffunction-sections -Wl,-gc-sections 的效果,这里是另一个代码:

  #include  #include< vector> 
#include< tuple>
#include< algorithm>
#include< chrono>
#include< windows.h>

#undef min

struct Point {
int x,y;
};

constexpr int length = 5;
constexpr int half_length(){
返回长度& 1?长度:长度 - 1;
}

模板< class F>
int func_template(F&& f){
#ifdef _MSC_VER
puts(__ FUNCSIG__);
#else
puts(__ PRETTY_FUNCTION__);
#endif
printf(\\\
);
return f();
}

struct fake_func {
int operator()()const {return 59; };
};

模板< class F,class ... Args>
int pass_args(F&& f,Args&& amp; args){
#ifdef _MSC_VER
puts(__ FUNCSIG__);
#else
puts(__ PRETTY_FUNCTION__);
#endif
printf(\\\
);
返回f(std :: forward< Args>(args)...);
}

模板< class T>
T min(T x){
return x;
}

模板< class T,class ... Args>
T min(T x,Args ... args){
T y = min(args ...);
return x < y? x:y;
}

void type_verifier(int x){
printf(%dd,x);
}

void type_verifier(char x){
printf('%c',x);
}

void type_verifier(double x){
printf(%lff,x);
}

模板< class T>
void type_verifier(T x){
printf(unknown);
}

模板< class T,class ... Args>
void type_verifier(T x,Args ... args){
type_verifier(x);
type_verifier(args ...);
}

int bufLen;
char buf [100];

模板< class ...参数>
inline int send(Args ... args){
bufLen = sprintf(buf,std :: forward< Args>(args)...);
返回bufLen;


namespace std {

内联命名空间v1 {
void func(){
printf(我是v1 \\\
);



namespace v2 {
void func(){
printf(我是v2\\\
);




$ b int main(){
std :: vector< int> v {1,2,3,4,5}; (auto& i:v)printf(%d,i)为
;
printf(\\\
);

点p {1,2};
printf(%d%d \\\
,p.x,p.y);

auto t = std :: make_tuple(Hello World,12);
printf(%s%d \ n,std :: get(t),std :: get(t));
int a,b;
auto f = [](){return std :: make_tuple(1,2); };
std :: tie(a,b)= f();
printf(%d%d \ n,a,b);

// int test_constexpr [half_length()+ 4];

int ft = func_template([] {return 42;});
printf(func_template:%d \\\
,ft);
ft = func_template(fake_func {});
printf(func_template:%d \\\
,ft);
ft = pass_args([](int x,int y){return x + y;},152,58);
printf(pass_args:%d \\\
,ft);
ft = pass_args([](int n,const char * m){
for(int i = 0; i< n; i ++)printf(%c,m [i]) ;
printf(\\\
);
return 0;
},5,Hello);

printf(min:%d \\\
,min(3,4,2,1,5));
type_verifier(12,'A',0.5,Hello);
printf(\\\
);

/ * send(Hello World);
send(%d,1);
send(%d,1234);
sprintf(buf,%d,123); * /

std :: func();
std :: v1 :: func();
std :: v2 :: func();

std :: rotate(v.begin(),v.begin()+ 2,v.end()); (auto& i:v)printf(%d,i)为
;
printf(\\\
);

auto start = std :: chrono :: steady_clock :: now();

std :: vector< int> x {2,4,2,0,5,10,7,3,7,1};
printf(插入排序:); (auto& i:x)printf(%d,i)为
;
printf(\\\
);
//插入排序
for(auto i = x.begin(); i!= x.end(); ++ i){
std :: rotate(std :: upper_bound (x.begin(),i,* i),i,i + 1); (auto& j:x)printf(%d,j)为
;
printf(\\\
);
}

std :: vector< int>堆{7,5,3,4,2};
std :: make_heap(heap.begin(),heap.end());
std :: pop_heap(heap.begin(),heap.end());
printf(Pop heap(%d)\\\
,heap.back());
heap.pop_back();
heap.push_back(1);
std :: push_heap(heap.begin(),heap.end());
std :: sort_heap(heap.begin(),heap.end()); (auto& i:heap)printf(%d,i)
;
printf(\\\
);

auto end = std :: chrono :: steady_clock :: now();
auto diff = end - start;
printf(time:%I64d ms\\\

std :: chrono :: duration_cast< std :: chrono :: milliseconds>(diff).count());

{
auto u = v;
std :: move_backward(u.begin(),u.begin()+ u.size() - 1,u.begin()+ u.size()); (auto& i:u)printf(%d,i)为
;
printf(\\\
);
}

{
auto u = v;
std :: move(u.begin()+ 1,u.begin()+ u.size(),u.begin()); (auto& i:u)printf(%d,i)为
;
printf(\\\
);
}

start = std :: chrono :: steady_clock :: now();
睡眠(2000);
end = std :: chrono :: steady_clock :: now();
diff = end - start;
printf(time:%I64d ms\\\

std :: chrono :: duration_cast< std :: chrono :: milliseconds>(diff).count());

std :: chrono :: steady_clock :: time_point before;
before = std :: chrono :: steady_clock :: now();
睡眠(2000);
auto后= std :: chrono :: steady_clock :: now();
printf(%f seconds\\\
,std :: chrono :: duration< double>(after - before).count());

返回0;
}

令我失望的是,最终的程序再次大于2MB。



有趣的是, cl.exe 谨慎地移除所有 iostream 相关函数即使我没有使用 / O2 或任何其他标志,只是 cl.exe main.cpp 。 (对于上面的代码, cl.exe 会生成100 + KB二进制文件)。



我是否错过任何其他有用的gcc的标志是什么?



规格:


  • Mingw-w64 gcc 6.1。 0

  • Mingw-w64 gcc 6.2.0

  • Visual Studio 2017 RC

  • 所有二进制文件都静态链接



与Linux比较

上面的代码(除了 windows.h ),gcc 4.9.2(Linux)和gcc 4.9.3(mingw-w64)睡眠已被删除)。
$ b

编译标记

g ++ -o c ++ 11 c ++ 11.cpp -std = c ++ 11 -static-libgcc -static-libstdc ++ -ffunction-sections -Wl,-gc-sections -O2



Linux gcc成功剥离了 iostream 和函数,而不需要 -flto 而Mingw-w64 gcc无法正常运行。



Windows只支持PE格式,而Linux支持端口ELF格式,允许Linux使用Gold连接器。也许这是解释?



更新



我最终在 https://sourceforge.net/p/mingw-w64/bugs/578/ 。让我们希望得到一些关注!

解决方案

尝试通过 -Wl从静态libstdc ++剥离调试和符号信息, - 带状所有。这使我的可执行文件在Cygwin(13x)上从9M减少到了670K,在Ubuntu(80x)上从6M减少到了80K。

I noticed that sometimes even if I don't use iostream and related I/O libraries, my binaries produced by Mingw were still unreasonably large.

For example, I wrote a code to use vector and cstdio only and compiled it with -O2 -flto, my program can go as large as 2MB! I run nm main.exe > e.txt and was shocked to see all the iostream related functions in it.

After some googling, I learnt to use -ffunction-sections -Wl,-gc-sections, that reduces the program size from 2MB to ~300KB (if with -s, 100+KB). Excellent!

To further test the effect of -ffunction-sections -Wl,-gc-sections, here is another code:

#include <cstdio>
#include <vector>
#include <tuple>
#include <algorithm>
#include <chrono>
#include <windows.h>

#undef min

struct Point {
    int x, y;
};

constexpr int length = 5;
constexpr int half_length() {
    return length & 1 ? length : length - 1;
}

template<class F>
int func_template(F&& f) {
#ifdef _MSC_VER
    puts(__FUNCSIG__);
#else
    puts(__PRETTY_FUNCTION__);
#endif
    printf("\n");
    return f();
}

struct fake_func {
    int operator()() const { return 59; };
};

template<class F, class... Args>
int pass_args(F&& f, Args&&... args) {
#ifdef _MSC_VER
    puts(__FUNCSIG__);
#else
    puts(__PRETTY_FUNCTION__);
#endif
    printf("\n");
    return f(std::forward<Args>(args)...);
}

template<class T>
T min(T x) {
    return x;
}

template<class T, class... Args>
T min(T x, Args... args) {
    T y = min(args...);
    return x < y ? x : y;
}

void type_verifier(int x) {
    printf("%dd ", x);
}

void type_verifier(char x) {
    printf("'%c' ", x);
}

void type_verifier(double x) {
    printf("%lff ", x);
}

template<class T>
void type_verifier(T x) {
    printf("unknown ");
}

template<class T, class... Args>
void type_verifier(T x, Args... args) {
    type_verifier(x);
    type_verifier(args...);
}

int bufLen;
char buf[100];

template<class... Args>
inline int send(Args... args) {
    bufLen = sprintf(buf, std::forward<Args>(args)...);
    return bufLen;
}

namespace std {

inline namespace v1 {
    void func() {
        printf("I am v1\n");
    }
}

namespace v2 {
    void func() {
        printf("I am v2\n");
    }
}

}

int main() {
    std::vector<int> v {1, 2, 3, 4, 5};
    for (auto &i : v) printf("%d ", i);
    printf("\n");

    Point p {1, 2};
    printf("%d %d\n", p.x, p.y);

    auto t = std::make_tuple("Hello World", 12);
    printf("%s %d\n", std::get<0>(t), std::get<1>(t));
    int a, b;
    auto f = []() { return std::make_tuple(1, 2); };
    std::tie(a, b) = f();
    printf("%d %d\n", a, b);

    //int test_constexpr[half_length() + 4];

    int ft = func_template([]{ return 42; });
    printf("func_template: %d\n", ft);
    ft = func_template(fake_func {});
    printf("func_template: %d\n", ft);
    ft = pass_args([](int x, int y) { return x + y; }, 152, 58);
    printf("pass_args: %d\n", ft);
    ft = pass_args([](int n, const char *m) {
        for (int i = 0; i < n; i++) printf("%c ", m[i]);
        printf("\n");
        return 0;
    }, 5, "Hello");

    printf("min: %d\n", min(3, 4, 2, 1, 5));
    type_verifier(12, 'A', 0.5, "Hello");
    printf("\n");

/*  send("Hello World");
    send("%d", 1);
    send("%d", "1234");
    sprintf(buf, "%d", "123");*/

    std::func();
    std::v1::func();
    std::v2::func();

    std::rotate(v.begin(), v.begin() + 2, v.end());
    for (auto &i : v) printf("%d ", i);
    printf("\n");

    auto start = std::chrono::steady_clock::now();

    std::vector<int> x {2, 4, 2, 0, 5, 10, 7, 3, 7, 1}; 
    printf("insertion sort: ");
    for (auto &i: x) printf("%d ", i);
    printf("\n");
    // insertion sort
    for (auto i = x.begin(); i != x.end(); ++i) {
        std::rotate(std::upper_bound(x.begin(), i, *i), i, i+1);
        for (auto &j: x) printf("%d ", j);
        printf("\n");
    }

    std::vector<int> heap {7, 5, 3, 4, 2};
    std::make_heap(heap.begin(), heap.end());
    std::pop_heap(heap.begin(), heap.end());
    printf("Pop heap (%d)\n", heap.back());
    heap.pop_back();
    heap.push_back(1);
    std::push_heap(heap.begin(), heap.end());
    std::sort_heap(heap.begin(), heap.end());
    for (auto &i: heap) printf("%d ", i);
    printf("\n");

    auto end = std::chrono::steady_clock::now();
    auto diff = end - start;
    printf("time: %I64d ms\n",
        std::chrono::duration_cast<std::chrono::milliseconds>(diff).count());

    {
        auto u = v;
        std::move_backward(u.begin(), u.begin() + u.size() - 1, u.begin() + u.size());
        for (auto &i : u) printf("%d ", i);
        printf("\n");
    }

    {
        auto u = v;
        std::move(u.begin() + 1, u.begin() + u.size(), u.begin());
        for (auto &i : u) printf("%d ", i);
        printf("\n");
    }

    start = std::chrono::steady_clock::now();
    Sleep(2000);
    end = std::chrono::steady_clock::now();
    diff = end - start;
    printf("time: %I64d ms\n",
        std::chrono::duration_cast<std::chrono::milliseconds>(diff).count());

    std::chrono::steady_clock::time_point before;
    before = std::chrono::steady_clock::now();
    Sleep(2000);
    auto after = std::chrono::steady_clock::now();
    printf("%f seconds\n", std::chrono::duration<double>(after - before).count());

    return 0;
}

To my disappointment, the final program is once again > 2MB.

Interestingly, cl.exe thoughtfully remove all iostream related functions consistently even if I didn't use /O2 or any other flags, just cl.exe main.cpp. (For the code above, cl.exe produces 100+KB binary).

Did I miss any other useful gcc flags for this?

Specification:

  • Mingw-w64 gcc 6.1.0
  • Mingw-w64 gcc 6.2.0
  • Visual Studio 2017 RC
  • All binaries are linked statically

Compare with Linux

I compared the binaries produced by gcc 4.9.2 (Linux) and gcc 4.9.3 (mingw-w64) for the above code (except windows.h and Sleep were removed).

Compile flag

g++ -o c++11 c++11.cpp -std=c++11 -static-libgcc -static-libstdc++ -ffunction-sections -Wl,-gc-sections -O2

Linux gcc did successfully strip away iostream and functions without the need for -flto while Mingw-w64 gcc just can't do it properly.

Windows only support PE format while Linux supports ELF format, allowing Linux to use Gold linker. Maybe this is the explanation?

Update

I eventually filed a bug at https://sourceforge.net/p/mingw-w64/bugs/578/ . Let's hope it gets some attentions!

解决方案

Try stripping debug and symbol info from static libstdc++ via -Wl,--strip-all. This reduced my executable from 9M to 670K on Cygwin (13x) and from 6M to 80K on Ubuntu (80x).

这篇关于gcc:去除未使用的功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆