2014-09-25 45 views
4

我正在使用C++進行大學研究項目,其中有很多模板,它們有更多嵌套模板等等。該項目是針對特定研究領域的有效索引數據結構。你可以想象:一個索引結構有很多參數需要調整,所以我們過多地使用了模板參數。當然,我們想用不同的參數集來測試我們的索引,所以有很多模板實例化。如何使C++鏈接佔用更少的內存

該項目並不是那麼龐大。也許50k LOC。但是,鏈接需要50秒,並且消耗超過7 GB的內存(!!!)。我在32GB的工作站上,所以對我來說一切都很好。我經常有這個項目的學士和碩士學生。問題是他們經常在4或8 GB RAM的筆記本電腦上工作。因此,這些學生在編譯該項目時遇到了很大的麻煩。得到的測試二進制文件(即僅包含索引結構的單元測試的二進制文件)爲700兆字節。 大部分都是符號,因爲嵌套模板會產生巨大的名字。如果我在二進制文件上使用strip,則它會下降到8兆字節。

那麼有沒有辦法在連接期間減少內存使用量?有沒有辦法讓嵌套模板具有更小的符號?

我們在Ubuntu 14.10下使用g ++ 4.9與std=c++11進行編譯。

編輯:

它真的好像是嵌套模板。我們有兩個測試用例,其中嵌套的模板非常深。用於這些測試的兩個.o文件構成了最終二進制文件的將近90%的內存。它們導致長度超過3000個字符的方法名稱。沒有辦法在這裏不使用嵌套模板,因爲它們形成示例查詢的「處理樹」。使用深度嵌套模板時,有什麼方法可以縮短名稱長度嗎?

+1

什麼操作系統,編譯器版? – EdChum 2014-09-25 10:07:12

+0

@EdChum:Ubuntu 14.10下的g ++ 4.9。現在將它添加到問題中。 – gexicide 2014-09-25 10:08:18

+0

哪個C++版本? C++ 11有外部模板! – 2014-09-25 10:08:20

回答

2

GCC對它使用的RAM有一個垃圾回收機制。

參數ggc-min-expandggc-min-heapsize用於確定GCC何時清理和釋放未使用的內存(它們的默認值是系統總內存的百分比)。

你可以嘗試這樣的:

g++ --param ggc-min-expand=0 --param ggc-min-heapsize=8192 

從GCC手冊:

GGC-分鐘-擴大

GCC使用垃圾收集器來管理它自己的內存分配。此參數指定垃圾收集器堆允許在收集之間擴展的最小百分比。 調整這可能會提高編譯速度;它對代碼 世代沒有影響。

當RAM> = 1GB時,默認值爲30%+ 70%*(RAM/1GB),上限爲100%。如果getrlimit可用,則「RAM」的概念是實際RAM,RLIMIT_RSS,RLIMIT_DATA和RLIMIT_AS中最小的一個。如果GCC 無法計算特定平臺上的RAM,則使用30%的下限 。將此參數和ggc-min-heapsize設置爲零將導致在每個機會都會發生完整收集。這是非常緩慢的 ,但可用於調試。

GGC-最小堆大小

垃圾收集器的堆的最小尺寸就開始 困擾以收集垃圾之前。第一次收集發生在 堆擴展超過ggc-min-heapsize的ggc-min-expand%之後。再次,調整 這可能會提高編譯速度,並且對代碼 生成沒有影響。 默認值爲RAM/8,下限爲4096(4兆字節),上限爲131072(128兆字節)。如果getrlimit爲 ,則「RAM」的概念是實際RAM中的最小值,即RLIMIT_RSS,RLIMIT_DATA和RLIMIT_AS。如果GCC無法在特定平臺上計算 RAM,則使用下限。設置這個 非常大的參數會有效地禁用垃圾收集。將此參數設置爲 ,ggc-min-expand爲零會導致完全收集到 發生在每個機會。

進一步瞭解詳細:

+0

,雖然它可能不是主要問題,但這種方法不會減少鏈接時間,但最有可能增加一點點 – 2014-09-25 10:54:27

+0

您是對的,鏈接時間和內存消耗之間存在權衡。此外,「最佳」值可能會有所不同。 – manlio 2014-09-25 13:06:47

3

那麼,有沒有降低聯動期間的內存使用的方法嗎?有沒有辦法讓嵌套模板具有更小的符號?

您是否考慮過在客戶端代碼中使用pimpl習語

考慮這樣你有這種包括鏈的情況:

啊 - >了Bh - >章 - >迪拉姆(C包括d,B包括C,等)

假設阿定義類AA, Bh定義了B類等等(用BB表示AA,用CC表示BB等)。

如果DD是一個大的模板和CC的實現中使用的模板代碼會被編譯三次,編譯單元A,B和C

現在,考慮會發生什麼,如果代替章包括Dh,您有以下情況:

Ch foward聲明CCImpl *pimpl並將其所有方法轉發到pImpl->方法(並且不包括Dh)。

C.cpp包括C.h和D.h,並執行CCImplCC

現在,D將包括一次(並編譯一次,爲C.cpp)。 A和B將只包含C.h和CImpl轉發聲明。 A.h,B.h和C.h不再知道模板存在。

+0

我考慮過這個,謝謝。問題在於我們的大學項目是關於衡量這些非常低效的數據結構的性能。因此,我們甚至無法支付額外的退化費用(以及隨之而來的內聯失敗的可能性)。我們有時候甚至會計算平均週期數,每次操作只有幾個週期。 – gexicide 2014-09-25 11:31:33

1

明智地使用繼承。您可能有一個class Foo<1,4,8,1,9,int, std::string>作爲class Bar的基類,然後該對象文件將只提及Bar

請注意,typedef不會爲鏈接目的引入名稱。

[編輯] ,解決因其他評論的性能問題,一個空的派生類中增加了對通用的編譯器沒有任何開銷正常的優化級別(常常甚至還有在調試版本中沒有開銷)