Я заметил, что иногда даже если я не использую iostream
и связанные библиотеки ввода-вывода, мои двоичные файлы, созданные Mingw, все еще были необоснованно большими.gcc: Strip unused functions
Например, я написал код для использования только vector
и cstdio
и скомпилировал его с помощью -O2 -flto
, моя программа может достигать 2 МБ! Я запустил nm main.exe > e.txt
и был потрясен, увидев все связанные с ним функции iostream
.
После некоторого поиска в Google, я научился использовать -ffunction-sections -Wl,-gc-sections
, что уменьшает размер программы от 2 МБ до ~ 300 КБ (если с -s
, 100 + КБ). Отлично!
Для дальнейшей проверки эффекта -ffunction-sections -Wl,-gc-sections
, вот еще один код:
#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;
}
К моему разочарованию, окончательная программа еще раз> 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
Я сравнил двоичные файлы, произведенные gcc 4.9.2 (Linux) и gcc 4.9.3 (mingw-w64) для вышеуказанного кода (кроме windows.h
и Sleep
).
Compile флаг
g++ -o c++11 c++11.cpp -std=c++11 -static-libgcc -static-libstdc++ -ffunction-sections -Wl,-gc-sections -O2
Linux НКУ действительно успешно стирает iostream
и функцию без необходимости -flto
в то время как Mingw-w64 НКА просто не может сделать это правильно.
Windows поддерживает только формат PE, в то время как Linux поддерживает формат ELF, позволяя Linux использовать Gold-линкер. Может быть, это объяснение?
Update
я в конце концов подал ошибку в https://sourceforge.net/p/mingw-w64/bugs/578/. Будем надеяться, что это привлечет внимание!
Это может помочь: [Как удалить неиспользуемые символы C/C++ с помощью GCC и ld?] (Https: // stackoverflow.com/questions/6687630/how-to-remove-unused-cc-symbols-with-gcc-and-ld) – benbuck
Пробовал все: -O (уменьшить 2KB), -fwhole-program (без изменений), -fomit-frame -поинтер (без изменений). -why_live недоступен. –
'длина & 1? length: length - 1' можно изменить на 'length + (length & 1) - 1' –