2009-11-29 98 views
2

我有一個自動生成的C++源文件,大小約爲40 MB。它主要由push_back命令組成,用於某些向量和字符串常量,這些命令將被推送。gcc/g ++:編譯大文件時出錯

當我嘗試編譯這個文件時,g ++退出並且說它無法保留足夠的虛擬內存(大約3 GB)。谷歌搜索這個問題,我發現,使用命令行開關

--param ggc-min-expand=0 --param ggc-min-heapsize=4096 

可以解決該問題。然而,他們似乎只在打開優化時才起作用。

1)這真的是我正在尋找的解決方案嗎?

2)還是有一個更快,更好(編譯需要年齡與這些選項激活)的方式來做到這一點?

最良好的祝願,

亞歷山大

更新:感謝所有的好點子。我嘗試了其中的大部分。使用數組而不是幾次push_back()操作減少了內存使用量,但是由於我試圖編譯的文件非常大,所以只會在稍後崩潰。從某種意義上說,這種行爲非常有趣,因爲在這樣的環境中沒有太多需要優化的地方 - 海灣合作委員會在幕後花費了多少內存? (我也停用所有優化進行編譯並得到相同的結果)

我現在切換到的解決方案是從原始文件中使用objcopy創建的二進制對象文件讀取原始數據。這是我原本不想做的事情,因爲用更高級別的語言(在這種情況下爲Perl)創建數據結構比在C++中執行此操作更方便。

但是,在Win32下運行它比預期的更復雜。 objcopy似乎以ELF格式生成文件,而且當我手動將輸出格式設置爲pe-i386時,似乎我已經消失的一些問題消失了。目標文件中的符號是按照文件名後面的標準命名的,例如,轉換文件inbuilt_training_data.bin將導致這兩個符號:binary_inbuilt_training_data_bin_start和binary_inbuilt_training_data_bin_end。我在網上發現了一些教程,聲稱這些符號應該被宣佈爲extern char _binary_inbuilt_training_data_bin_start;,但這看起來並不正確 - 只有extern char binary_inbuilt_training_data_bin_start;爲我工作。

+4

這屬於thedailywtf.com。 – 2009-11-29 23:57:16

+1

我們應該讓他發佈代碼嗎? :-) – paxdiablo 2009-11-30 00:05:10

+0

我知道Markdown JavaScript開始對我的CPU徵稅,因爲我在幾KB後打字;我想像40 MB會給我一個旋轉的死亡彩虹。 – 2009-11-30 00:30:28

回答

4

改爲使用常數數據表代替。例如,而不是這樣做:

void f() { 
    a.push_back("one"); 
    a.push_back("two"); 
    a.push_back("three"); 
    // ... 
} 

嘗試這樣做:

const char *data[] = { 
    "one", 
    "two", 
    "three", 
    // ... 
}; 

void f() { 
    for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); i++) { 
     a.push_back(data[i]); 
    } 
} 

編譯器很可能會更有效產生較大的常量數據表,而不是包含許多push_back()通話巨大的作用。

1

你可以做同樣的問題,而不生成40 MB的C++?這比我使用的一些操作系統更重要。一個循環和一些數據文件,也許?

+0

循環和數據文件...你能擴展你的答案嗎?也許有一個簡單的例子。有可能他無法在任何地方使用一句提示... – 2009-11-30 00:14:26

1

這聽起來像你自動生成的應用程序是這樣的:

push_back(data00001); 
... 
push_back(data99999); 

你爲什麼不把數據放到一個外部文件,並讓程序讀取一個循環這個數據?

0

如果你只是在一排生成調用的衝頭push_back(),你可以重構它弄成這個樣子:

// Old code: 
v.push_back("foo"); 
v.push_back("bar"); 
v.push_back("baz"); 

// Change that to this: 
{ 
    static const char *stuff[] = {"foo", "bar", "baz"}; 
    v.insert(v.end(), stuff, stuff + ARRAYCOUNT(stuff)); 
} 

ARRAYCOUNT在宏定義如下:

#define ARRAYCOUNT(a) (sizeof(a)/sizeof(a[0])) 

大括號的額外級別只是爲了避免名稱衝突,如果你有很多這樣的塊;或者,您可以爲stuff佔位符生成一個新的唯一名稱。

如果這仍然不起作用,我建議將源文件分成許多較小的源文件。如果你有很多獨立的功能,這很簡單;如果你有一個巨大的功能,你將不得不努力工作,但它仍然是非常可行的。

0

爲了補充這裏的一些答案,你可能會更好地生成一個二進制對象文件並直接鏈接它 - 而不是編譯由const char[]組成的文件。

我最近在使用gcc時遇到了類似的問題。 (將大約60 MB的PNG數據分割成大約100個頭文件。)將它們全部包括在內是最糟糕的選擇:所需內存量似乎隨着編譯單元的大小呈指數增長。

+1

您應該將PNG數據保存在源文件中,而不是標題中。頭文件應該只有'extern const char img_data []; extern const size_t img_data_size;'和源文件應該有'char img_data [] = {...}; const size_t img_data_size = sizeof(img_data);'編譯器處理起來更容易,使用圖像數據的文件在圖像更改時不需要重新編譯。 – 2009-11-30 00:28:58

+0

@Adam Rosenfeld:這是行得通的,但是這可能是一種破解,它不會解決實際的問題,即首先通過編譯器的二進制流。 (二進制數據 - > C源代碼 - >編譯器 - >二進制數據 - 聽起來確實不錯,是嗎?)順便說一下,'鏈接器'解決方案看起來完全像你的:頭部只包含extern char * +外部大小。 – aib 2009-11-30 01:51:06

+0

...我想我在MacOS X上編譯時,其鏈接器是不同的,編譯器套件沒有明顯的將二進制數據轉換爲目標文件的方式。但只要你有一個包含數據開始+數據大小的兩個符號的對象文件(或者數據開始+數據結束,它可能已經),那麼創建它的人是什麼以及如何創建它並不重要? – aib 2009-11-30 02:02:48

0

如果您無法重構代碼,您可以嘗試增加交換空間量,前提是您的操作系統支持較大的地址空間。這應該適用於64位計算機,但對於32位系統,3 GB可能太多了。