2015-03-08 89 views
4

我想用std::regex(標準正則表達式庫)編譯一個小型的C++程序。
編譯器:GCC /克++ 4.9.2在Fedora 21.巨大的程序大小C++與std :: regex

#include <string.h> 
#include <iostream> 
#include <regex> 
using namespace std; 
int main() { 
cout << "Content-Type: text/html\n\n"; 
    std::string s ("there is a subsequence in the string\n"); 
    std::regex e ("\\b(sub)([^ ]*)"); // matches words beginning by "sub" 
    std::cout << std::regex_replace (s,e,"sub-$2"); 
} 

這是不可能用std::regex編譯程序而不-std=c++11,所以對於在終端編譯合適的指令是:

g++ -std=c++11 code.cpp -o prog 

我主要問題是:源代碼非常小,但爲什麼編譯後程序的最終文件大小如此巨大:480千字節?

是否因爲-std=c++11的影響?

發生了什麼以及如何縮小最終二進制程序的大小?

UPD1。

使用-Os標誌實際上是將std :: regex的程序大小從480 KB降低到100-120 KB的好方法。 但奇怪的是,對於C/C++程序,使用std :: regexp的優化文件甚至超過了標準的7-12 kylobytes,而且只有很少的字符串源代碼。 例如,有可能把同樣的正則表達式與RE2正則表達式庫替換招(Fedora的21 「百勝安裝RE2-devel的」)在8.5 KB二進制文件:

#include <string.h> 
#include <iostream> 
#include "re2/re2.h" 
using namespace std; 
int main() { 
cout << "Content-Type: text/html\n\n"; 
std::string s ("there is a subsequence in the string\n"); 
RE2::GlobalReplace(&s, "\\b(sub)([^ ]*)", "sub-\\2"); 
cout << s; 
} 

編譯時:

g++ -lre2 code.cpp -o prog 

UPD2。

objdump的用於標準::正則表達式的程序:

0 .interp 00000013 08048154 08048154 00000154 2**0 
1 .note.ABI-tag 00000020 08048168 08048168 00000168 2**2 
2 .note.gnu.build-id 00000024 08048188 08048188 00000188 2**2 
3 .gnu.hash 000000b0 080481ac 080481ac 000001ac 2**2 
4 .dynsym 000006c0 0804825c 0804825c 0000025c 2**2 
5 .dynstr 00000b36 0804891c 0804891c 0000091c 2**0 
6 .gnu.version 000000d8 08049452 08049452 00001452 2**1 
7 .gnu.version_r 000000d0 0804952c 0804952c 0000152c 2**2 
8 .rel.dyn 00000038 080495fc 080495fc 000015fc 2**2 
9 .rel.plt 000002b8 08049634 08049634 00001634 2**2 
10 .init 00000023 080498ec 080498ec 000018ec 2**2 
11 .plt 00000580 08049910 08049910 00001910 2**4 
12 .text 0001f862 08049e90 08049e90 00001e90 2**4 
13 .fini 00000014 080696f4 080696f4 000216f4 2**2 
14 .rodata 00000dc8 08069740 08069740 00021740 2**6 
15 .eh_frame_hdr 00003ab4 0806a508 0806a508 00022508 2**2 
16 .eh_frame 0000f914 0806dfbc 0806dfbc 00025fbc 2**2 
17 .gcc_except_table 00000e00 0807d8d0 0807d8d0 000358d0 2**2 
18 .init_array 00000008 0807feec 0807feec 00036eec 2**2 
19 .fini_array 00000004 0807fef4 0807fef4 00036ef4 2**2 
20 .jcr 00000004 0807fef8 0807fef8 00036ef8 2**2 
21 .dynamic 00000100 0807fefc 0807fefc 00036efc 2**2 
22 .got 00000004 0807fffc 0807fffc 00036ffc 2**2 
23 .got.plt 00000168 08080000 08080000 00037000 2**2 
24 .data 00000240 08080180 08080180 00037180 2**6 
25 .bss 000001b4 080803c0 080803c0 000373c0 2**6 
26 .comment 0000002c 00000000 00000000 000373c0 2**0 

objdump的用於RE2程序:

0 .interp 00000013 08048154 08048154 00000154 2**0 
1 .note.ABI-tag 00000020 08048168 08048168 00000168 2**2 
2 .note.gnu.build-id 00000024 08048188 08048188 00000188 2**2 
3 .gnu.hash 00000034 080481ac 080481ac 000001ac 2**2 
4 .dynsym 00000180 080481e0 080481e0 000001e0 2**2 
5 .dynstr 00000298 08048360 08048360 00000360 2**0 
6 .gnu.version 00000030 080485f8 080485f8 000005f8 2**1 
7 .gnu.version_r 000000a0 08048628 08048628 00000628 2**2 
8 .rel.dyn 00000010 080486c8 080486c8 000006c8 2**2 
9 .rel.plt 00000090 080486d8 080486d8 000006d8 2**2 
10 .init 00000023 08048768 08048768 00000768 2**2 
11 .plt 00000130 08048790 08048790 00000790 2**4 
12 .text 00000332 080488c0 080488c0 000008c0 2**4 
13 .fini 00000014 08048bf4 08048bf4 00000bf4 2**2 
14 .rodata 00000068 08048c08 08048c08 00000c08 2**2 
15 .eh_frame_hdr 00000044 08048c70 08048c70 00000c70 2**2 
16 .eh_frame 0000015c 08048cb4 08048cb4 00000cb4 2**2 
17 .gcc_except_table 00000028 08048e10 08048e10 00000e10 2**0 
18 .init_array 00000008 08049ee4 08049ee4 00000ee4 2**2 
19 .fini_array 00000004 08049eec 08049eec 00000eec 2**2 
20 .jcr 00000004 08049ef0 08049ef0 00000ef0 2**2 
21 .dynamic 00000108 08049ef4 08049ef4 00000ef4 2**2 
22 .got 00000004 08049ffc 08049ffc 00000ffc 2**2 
23 .got.plt 00000054 0804a000 0804a000 00001000 2**2 
24 .data 00000004 0804a054 0804a054 00001054 2**0 
25 .bss 00000090 0804a080 0804a080 00001058 2**6 
26 .comment 0000002c 00000000 00000000 00001058 2**0 

主要區別是在12.text:在第一種情況下使用尺寸 - 0001f862(129122);第二 - 只有00000332(818)。

+3

爲什麼你認爲這是巨大的?你期望什麼?爲什麼? – Walter 2015-03-08 20:15:16

+1

運行'strip。/ prog'並檢查文件大小是否改變。 – 2015-03-08 20:26:25

+0

@Walter,我試圖在UPD1中說明問題。我期望帶有三行代碼的程序將以小二進制編譯,但std :: regex與-std = C++ 11會返回不成比例的巨大二進制結果。所以我知道:std :: regex是正常的,還是我對C++ 11或std :: regex缺乏瞭解。 – 2015-03-08 20:47:20

回答

8

在RE2的情況下,大多數的實際實現是在共享庫中,它不會成爲可執行文件的一部分。當您運行該程序時,它會分別加載到內存中。

如果是std::regex,這實際上只是std::basic_regex<char>的一個別名,它是一個模板。編譯器實例化模板並直接將其構建到您的程序中。儘管模板可以在共享庫內部實例化,但它們通常不是,並且std::basic_regex<char>不在您的案例中的共享庫中。

作爲一個例子。下面是如何創建一個單獨的共享庫與正則表達式實例:

main.cpp中:

#include <iostream> 
#include <string> 
#include "regex.hpp" 

int main() { 
    std::cout << "Content-Type: text/html\n\n"; 
    std::string s {"There is a subsequence in the string\n"}; 
    std::basic_regex<char> e {"\\b(sub)([^ ]*)"}; 
    std::cout << std::regex_replace (s,e,"sub-$2"); 
} 

regex.hpp:

#include <regex> 

extern template class std::basic_regex<char>; 

extern template std::string 
    std::regex_replace(
    const std::string&, 
    const std::regex&, 
    const char*, 
    std::regex_constants::match_flag_type 
); 

regex.cpp:

#include "regex.hpp" 

template class std::basic_regex<char>; 

template std::string 
    std::regex_replace(
    const std::string&, 
    const std::regex&, 
    const char*, 
    std::regex_constants::match_flag_type 
); 

構建步驟:

 
g++ -std=c++11 -Os -c -o main.o main.cpp 
g++ -std=c++11 -Os -c -fpic regex.cpp 
g++ -shared -o libregex.so regex.o 
g++ -o main main.o libregex.so -Wl,-rpath,. -L. -lregex 

在我的系統中,生成的文件大小爲:

 
     main: 13936 
libregex.so: 196936 
3

您可以通過設置優化減少大小:

g++ -std=c++11 -O3 -o prog code.cpp 

-O3標誌意味着最大優化。在我的機器中,可執行文件從519K減少到142K

您還可以使用-Os來優化大小。在我的機器上,進一步縮小到120k

g++ -std=c++11 -Os -o prog code.cpp 
+2

這樣做**不回答問題**。順便說一句,鏗鏘3.5 Mac OsX給103Kb與-O和104Kb與-O3。 – Walter 2015-03-08 20:09:31

+0

非常感謝你的回答!這實際上是減少程序大小的好方法。但奇怪的是,即使使用std :: regex超過15 kb的優化文件:在所有其他正則表達式庫(如RE2或PCRE; UPD1)之後,都不會從小的源代碼創建大的二進制文件。 現在看來,std :: regexp對於C/C++中的正則表達式並不是很好的解決方案(特別是如果認爲std :: regex在gcc/g ++ 4.9之前無法正常工作)。 – 2015-03-08 20:42:25

+1

@СергейИванопуло你正在向後看。將* another * regexp添加到代碼中,查看代碼大小如何更改。如果你的應用程序如此之大,以至於擔心它的大小,你將會擁有許多正則表達式,所以你關心的實際上是增量成本,而不是最初的成本。初始成本將攤銷在正則表達式的許多用途。是的,你**必須**從可執行文件中去除符號。 – 2015-03-08 21:27:52

4

首先,因爲它是在評論已經說了,你應該做的strip或使用size實用程序後測量優化的二進制的大小。否則,您會過分關注調試存儲在二進制文件中的信息。即使真的運行該二進制文件,該信息通常也不佔用RAM。

其次,實際的答案 - 大部分二進制大小來自正則表達式本身和std後面的其他東西。您可以使用readelf實用程序來檢查該實用程序,如:readelf -sW prog | c++filt - 顯示二進制文件中的所有函數及其大小。似乎相當大一部分的正則表達式實現留在了二進制文件中實例化的模板函數中。 GCC作者可能會在libstdc++中實例化更多,以允許共享,就像他們在處理其他一些事情時一樣。一些方法string

另一個不是很着名的二進制大小優化技術:在binutils的gold中實現的ICF(相同的代碼摺疊)。將-fuse-ld=gold -Wl,--icf=safe添加到您的鏈接器標誌。