【问题标题】:Memory optimization in PHP arrayPHP数组中的内存优化
【发布时间】:2011-09-14 06:11:41
【问题描述】:

我正在使用一个大型数组,它是一个高度图,1024x1024,当然,我遇到了内存限制。在我的测试机器中,如果我愿意,我可以将内存限制增加到 1gb,但在我只有 256 ram 的小型 VPS 中,这不是一个选项。

我一直在堆栈和谷歌中搜索,发现了几个“嗯,你使用 PHP 不是因为内存效率,放弃它并用 c++ 重写”老实说,没关系,我承认 PHP 喜欢内存。

但是,在深入研究 PHP 内存管理时,我没有发现每种数据类型消耗的内存。或者,如果转换为另一种类型的数据会减少内存消耗。

我发现的唯一“优化”技术是取消设置变量和数组,仅此而已。

使用一些 PHP 解析器将代码转换为 c++ 会解决问题吗?

谢谢!

【问题讨论】:

  • 数组在 PHP 中是真正需要内存的(因为它们实际上是字典)。如果您可以放弃一些(很多!)速度,您可以fake binary arrays like in C,我猜也适用于二维结构。但也许你真的想调查HipHop PHP to C++ compiler
  • 你需要减少多少内存使用?正如您所读到的,您可以在 PHP 中执行的内存管理方式很少。您可以进行一些“优化”,但可能没有什么可以根据您的需要进行削减。
  • PHP 中的每个变量都有与之相关的开销。不仅要存储变量的值,而且还要存储变量的名称、类型等……即使是简单的$x[1] = 2; 后面也有大量额外内容。
  • @mario 我想知道为什么链接的帖子在十六进制编码处停止并且不直接使用字符串的完整字节。看起来有点数学实际上可能更快......但我不使用 PHP(它不会考虑有效的多字节序列和诸如此类的东西:-)
  • @pst:我确实有另一个版本使用pack() 处理二进制字符串。但这并不是真的更快。只是节省了两倍的内存。 (在 PHP 中你可以伪造的只有这么多;)

标签: php arrays memory-management


【解决方案1】:

如果你想要一个真正的索引数组,请使用SplFixedArray。它使用更少的内存。此外,PHP 5.3 具有更好的垃圾收集器。

除此之外,PHP 将使用比更精心编写的 C/C++ 等价物更多的内存。

1024x1024 整数数组的内存使用情况:

  • 标准数组:218,756,848
  • SplFixedArray:92,914,208

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();

请注意,在 C 中使用 64 位整数的相同数组将是 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 以外的其他工具来处理这些数据。

【讨论】:

  • +1 此外,如果适用,模拟数组索引和使用打包可能会进一步减少内存使用量。例如。如果每个高度图值只有 8 位,则当打包到 32 位(或 64 位,取决于 PHP 位)时,内存使用量应该会大大减少。由于有效负载大小/利用率与所用 PHP 值的值维护开销,效率的确切增益会有所不同。 (我认为每个整数值有 4 个字节的“开销”,但我不完全确定。)
  • 显然有超过 4 个字节的开销 ...this post 建议它可能需要超过 36 个(或 x64 上的 72 个)字节,只是为了一个微不足道的值。这表明打包非常有益(就内存使用而言)。假设 8 位输入和 32 位拱门,4 个值将占用约 36 字节,而打包时约 144 字节,而在 x64 机器上,8 个值将占用约 72 字节和约 576 字节! (哎呀!)
  • 因此,总而言之......通过打包,8 位值被摊销到 ~9 个字节,用于对 9MB 对象开销/数据的即兴估计,不包括包含在数组本身等——将发布的数字四等分是~22.5MB 的总使用量。 (这样的打包可能看起来过度优化,但考虑到目标仅限于 256MB RAM .. ;-)
  • @pst,我添加了一些关于将数据打包成字符串的内容。在处理 8 位整数(可能是高度图)时,字符串整数数组的大小基本上与 C 等效项的大小相同。当然,速度会比原生整数差很多很多。
  • @konforce 打包成 PHP 整数值 :) 对于 许多 操作(只是一个掩码和一个额外的移位),速度应该接近未打包的速度——还有更多高效的内存(但不如拼凑成一个字符串高效)。
【解决方案2】:

除了在cmets中接受的答案和建议,我想建议PHP Judy array implementation

快速测试显示了有趣的结果。使用常规 PHP 数组数据结构的具有 100 万个条目的数组大约需要 200 MB。 SplFixedArray 使用大约 90 兆字节。朱迪使用 8 兆。权衡是在性能上,Judy 花费的时间大约是常规 php 数组实现时间的两倍。

【讨论】:

  • 我去看看,不错不错!就我而言,为了节省一些内存,我可以忍受性能下降。
  • 正是我需要的! Judy Array 太棒了。高性能和低内存使用。
  • @FlycKER - 我很高兴有人决定使用这个很棒的数组实现:)
  • Judy Array 不是只是在内部使用了一个打包的 PHP 字符串吗?
  • @N.B.,不,但似乎没有另一种在 PHP 中更有效地实现它的方法吗?
【解决方案3】:

聚会有点晚了,但是如果你有一个多维数组,当你将完整的数组存储为 json 时,你可以节省大量的 RAM。

$array = [];

$data = [];
$data["a"] = "hello";
$data["b"] = "world";

要存储这个数组,只需使用:

$array[] = json_encode($data);

而不是

$array[] = $data;

如果你想找回来,只需使用类似的东西:

$myData = json_decode($array[0], true);

我有一个包含 275.000 组的大数组,节省了大约 36% 的 RAM 消耗。

编辑: 当您压缩 json 字符串时,我发现了一种更好的方法:

$array[] = gzencode(json_encode($data));

并在需要时解压缩:

$myData = json_decode(gzdecode($array[0], true));

这为我节省了近 75% 的 RAM 峰值使用量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-30
    • 2023-03-08
    • 1970-01-01
    • 2010-11-09
    • 2020-08-09
    • 2015-12-09
    • 2017-02-06
    • 1970-01-01
    相关资源
    最近更新 更多