[英]Reading large (~1GB) data file with C++ sometimes throws bad_alloc, even if I have more than 10GB of RAM available
[英]Allocating ~10GB of vectors - how can I speed it up?
我正在加載約1000個文件,每個文件代表約300萬個浮動數組。 我需要將它們全部存儲在內存中,因為我需要進行一些涉及它們全部的計算。
在下面的代碼中,我詳細介紹了內存分配和文件讀取,因此可以分別觀察它們的速度。 我感到驚訝的是,發現內存分配花費的時間比讀取文件的時間長得多。
std::vector<std::vector<float> * > v(matrix_count);
for(int i=0; i < matrix_count; i++) {
v[i] = new std::vector<float>(array_size);
}
for(int i=0; i < matrix_count; i++) {
std::ifstream is(files[i]);
is.read((char*) &((*v[i])[0]), size);
is.close();
}
通過計算時間,分配循環花費了6.8s,而文件加載花費了2.5s。 看起來與從磁盤讀取空間比為磁盤分配空間快三倍,這是違反直覺的。
我可以做些什么來加快內存分配嗎? 我嘗試分配一個較大的向量,但是由於bad_malloc失敗,我猜不能使用10GB的向量。
Is there something I could do to speed up the memory allocation? I tried allocating one large vector instead, but that failed with bad_malloc -- I guess a 10GB vector isn't ok.
我主要想通過解決這一部分來做出回應: bad_alloc
異常易於被誤解。 它們不是“內存不足”的結果,而是系統找不到連續的未使用頁面塊的結果。 如果您習慣於嘗試分配大量連續內存塊的習慣,那么您可能擁有足夠多的可用內存,並且仍然會得到bad_alloc
,這僅僅是因為系統無法找到一組空閑的連續頁面。 您不一定通過“確保有足夠的可用內存”來避免bad_alloc
,因為您可能已經看到,嘗試分配僅10 GB的塊時,擁有超過100 GB的RAM仍會使您容易受到損壞。 避免它們的方法是在較小的塊中分配內存,而不是分配一個史詩數組。 在足夠大的規模上,展開列表之類的結構可以開始在巨大的數組上提供良好的性能,並且有一個更低的(呈指數形式)出現bad_alloc
異常的可能性,除非您實際上確實耗盡了所有可用的內存。 實際上存在一個峰值,在該峰值處連續性和它提供的引用局部不再變得有用,並且實際上可能以足夠大的尺寸(主要是由於分頁而不是緩存)阻礙了內存性能。
對於您要處理的史詩規模輸入,鑒於其具有頁面友好性,實際上您可能會從std::deque
獲得更好的性能(這是少數情況下deque可以真正發亮而無需push_front
vs. vector
)。 如果您不需要完美的連續性,可以嘗試一下。
自然,最好是使用實際的探查器進行測量。 它可以幫助我們深入研究實際的問題,盡管鑒於您“大量塊”的種類,您可能在這里受到內存而不是磁盤IO的瓶頸可能並不完全令人震驚(令人驚訝,但也許並不令人震驚)。正在分配(磁盤IO速度很慢,但是如果您真正給系統施加壓力,則內存堆分配有時可能會很昂貴)。 這在很大程度上取決於系統的分配策略,但是如果您分配這種史詩般的內存塊並進行批量分配,那么即使是平板分配器或伙伴分配器也可能會退回到一個慢得多的代碼分支,並且分配甚至可能開始需要類似於搜索或更多訪問的東西在那些極端情況下遷移到二級存儲(在這里,我不確定分配這么多大塊時到底是怎么回事,但是我已經“感覺到”並測量了這些瓶頸,但是在某種程度上我從來沒有弄清楚過OS到底在做什么-上面的段落純粹是推測。
在這里這有點違反直覺,但是分配大量較小的塊通常可以提高性能。 通常情況會使情況變得更糟,但是如果我們要談論的是每個內存塊300萬個浮點數和類似的1000個內存塊,則可能有助於開始分配頁面友好的4k塊。 通常,預先在大塊中預先分配內存並進行池化比較便宜,但是這種情況下的“大”更像是4 KB塊,而不是10 GB塊。
std::deque
通常會為您執行這種操作,因此嘗試查看是否有幫助可能是最快的操作。 使用std::deque
,您應該能夠為所有10 GB的內容制作一個文件,而不必將其拆分成較小的內容,以避免bad_alloc
。 它也沒有某些引用的全部內容的零初始化開銷,即使在最壞的情況下,它的push_backs
也是固定時間的(不像std::vector
那樣攤銷固定時間),所以我會請使用實際push_back
嘗試std::deque
,而不是對其進行預先大小調整並使用operator[]
。 您可以一次讀取一小塊文件的內容(例如:使用4k字節緩沖區),而只需推回浮點數即可。 無論如何都可以嘗試。
無論如何,這些都是未經培訓的猜測,沒有代碼和配置文件度量,但是這些都是在度量之后可以嘗試的一些方法。
MMF也可能是此方案的理想解決方案。 讓操作系統處理訪問文件內容所需的所有棘手細節。
使用多個線程進行內存分配和讀取文件。 您可以創建一組說15條線程,然后讓每個線程接下一個可用的作業。
當您深入研究時,您會發現打開文件也有相當大的開銷,使用多個線程可以大大減少開銷。
您不需要處理內存中的所有數據。 取而代之的是,您應該使用虛擬矢量之類的東西,在需要時加載所需的數據。 使用這種方法可以節省內存,並且不會給您帶來大量內存分配的副作用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.