2011-06-13 57 views
24

我正在處理一個大數組,這是一個高度圖,1024x1024,當然,我卡在內存限制內。在我的測試機器上,如果需要,我可以將內存限制提高到1GB,但在我的小型VPS中只有256內存,這不是一個選項。PHP數組中的內存優化

我一直在搜索堆棧和谷歌,並發現了幾個「好吧,你使用的PHP不是因爲內存效率,把它重寫在C++中」,說實話,沒關係,我認識到PHP喜歡內存。

但是,當挖掘更多內部PHP內存管理時,我沒有發現哪些內存會消耗每種數據類型。或者,如果投射到其他類型的數據可以減少內存消耗。

我發現的唯一「優化」技術是取消設置變量和數組,這就是它。

使用一些PHP解析器將代碼轉換爲C++可以解決問題嗎?

謝謝!

+3

數組在PHP中是真正的內存渴望(因爲它們實際上是字典)。如果你可以放棄一些(很多!)的速度,你可以[僞造二進制數組像C](http://stackoverflow.com/questions/5505124/cheating-php-integers/5505643#5505643),也可以用於2D結構我猜。但是,也許你真的想調查[HipHop PHP到C++編譯器](https://github.com/facebook/hiphop-php/wiki/)。 – mario 2011-06-13 21:25:37

+0

PHP中的每個變量都有與之相關的開銷。不僅變量的值必須被存儲,而且變量的名字,類型等等......即使是一個簡單的'$ x [1] = 2;'也有大量額外的東西在其後面。 – 2011-06-13 21:27:53

+0

怎麼樣一個PHP擴展? – Bytemain 2011-06-13 21:32:25

回答

44

如果你想要一個真正的索引數組,使用SplFixedArray。它使用較少的內存。此外,PHP 5.3有一個更好的垃圾收集器。

除此之外,PHP將使用比更精心編寫的C/C++等價物更多的內存。

內存使用1024×1024的整數數組:

  • 標準陣列:218756848
  • SplFixedArray:92914208

通過memory_get_peak_usage()

$array = new SplFixedArray(1024 * 1024); // array(); 
for ($i = 0; $i < 1024 * 1024; ++$i) 
    $array[$i] = 0; 

echo memory_get_peak_usage(); 

注意測量的相同的陣列中使用64位整數的C將是8M。

正如其他人所建議的那樣,您可以將數據打包爲一個字符串。這是較慢,但更高的內存。如果使用8個值是超級簡單:

$x = str_repeat(chr(0), 1024*1024); 
$x[$i] = chr($v & 0xff); // store value $v into $x[$i] 
$v = ord($x[$i]);  // get value $v from $x[$i] 

這裏的記憶將只大約1.5MB(即考慮PHP與眼前這個整數字符串數組整個開銷時)。

爲了好玩,我創建了一個創建1024x1024 8位整數的簡單基準,然後循環一次。打包版本全部使用ArrayAccess,以便用戶代碼看起來相同。

    mem write read 
array    218M 0.589s 0.176s 
packed array  32.7M 1.85s 1.13s 
packed spl array 13.8M 1.91s 1.18s 
packed string  1.72M 1.11s 1.08s 

打包陣列中使用本機64位整數(只包裝7個字節,以避免處理簽名的數據)和填充柱中使用的ordchr。很明顯,實施細節和計算機規格會影響一些東西,但我希望你能得到類似的結果。

所以,雖然數組速度提高了6倍,但它也使用了125倍的內存作爲下一個最佳選擇:打包字符串。顯然,如果內存不足,速度無關緊要。 (當我在沒有ArrayAccess類的情況下直接使用打包字符串時,它們只比本機陣列慢3倍。)

簡而言之,總結一下,如果速度有任何問題,我會用純PHP以外的東西來處理這些數據。

+0

+1此外,仿真數組索引並使用打包可能會進一步減少內存使用情況(如果適用)。例如。如果每個height-map值只有8位,當打包爲32位(或64位,取決於PHP位)時,*內存使用量應該大大減少*。效率的確切增益因負載大小/利用率與所使用的PHP值的維護開銷而有所不同。 (我認爲每個整數值有4個字節的「開銷」,但我不完全確定)。 – 2011-06-13 22:02:44

+0

顯然有超過4個字節的開銷... [此帖](http://stackoverflow.com/questions/5972170/what-is-overhead-of-using-php-int)表明它可能需要36個字節(或64個字節的72個字節),這只是一個微不足道的值。這表明它是*非常有利*(在內存使用方面)進行打包。假設8位輸入和32位拱,如果在x64機器上打包,則4個值需要〜36bytes,而〜144bytes需要8個值,大約需要72bytes〜576bytes! (Yikes!) – 2011-06-13 22:34:08

+0

因此,總而言之......包裝中,8位值被分攤到9個字節,用於9MB對象開銷/數據的非臨時性估計,不包括存儲在陣列本身中所需的內存,等 - 佔用的總數是〜22.5MB。 (這樣的包裝可能看起來過度優化,但是考慮到目標僅限於256MB RAM .. ;-) – 2011-06-13 22:49:00

11

除了在評論中接受的答案和建議,我想建議PHP Judy array implementation

快速測試顯示有趣的結果。使用常規PHP數組數據結構的數組有100萬個條目需要〜200 MB。 SplFixedArray使用大約90兆字節。茱蒂使用8 megs。權衡取決於性能,Judy需要的時間大約是常規php數組實現的兩倍。

+0

我想看看,真好!在我的情況下,爲了節省一些內存,我可以忍受性能上的衝擊。 – 2011-10-19 15:29:08

+0

正是我需要的! [Judy Array](http://en.wikipedia.org/wiki/Judy_array)很棒。高性能和低內存使用率。 – FlycKER 2012-12-12 13:11:41

+0

@FlycKER - 我很高興有人決定使用這個很棒的數組實現:) – 2012-12-12 13:46:47