2011-06-13 52 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