PHP中array_merge和union的性能比较

3
我想了解PHP中array_mergeunion的内部工作原理,以满足我的好奇心。请帮我了解以下ZEND内部实现:
  • array_merge(CPU和RAM较慢)
  • UNION(在所有方面都更快)
这是我运行的基准测试代码:https://gist.github.com/EnchanterIO/6e90f828c1b32c894d35267c353e83d2 使用PHP 7的输出结果为:

➜ ArrayMerge git:(master) ✗ php src/benchmark.php array_merge 20000

使用array_merge构建20000个元素的数组花费了7秒钟。

内存使用情况为:8 MB。内存峰值为:12288 MB。

➜ ArrayMerge git:(master) ✗ php src/benchmark.php union 20000

使用union构建20000个元素的数组花费了0秒钟。

内存使用情况为:8 MB。内存峰值为:10240 MB。

对于array_merge的理论:

我正在查看用于数组合并的C语言PHP源代码(如果这是正确的位置),尽管对我来说有点难以阅读,因为我不熟悉这些术语,但似乎array_merge较慢的原因是由于需要额外的foreach以及array_merge在结果数组中重新编号数字键。

对于UNION的理论:

虽然我没有找到它的源代码,但据我所知,将元素添加到数组中的方法如下:

  • 一开始,数组内部分配了一些内存
  • 通过向数组添加新元素,最终需要在后台动态分配更多内存,因此 PHP 内部(C)创建一个新数组,其内存量是原来的两倍,并复制内容
  • 通过添加更多元素,这个过程会重复
  • 没有重新排序,没有 foreach,只是随着时间的推移逐渐增加内存

有人能否详细解释这个过程并深入解释 ZEND 的幕后魔法?

更新:

我被引荐到以下链接,但仍无法理解:

https://lxr.room11.org/xref/php-src%40master/Zend/zend_opcode.c#740 https://lxr.room11.org/xref/php-src%40master/Zend/zend_operators.c#897 https://lxr.room11.org/xref/php-src%40master/Zend/zend_hash.c#1915


1
你正在比较的这两段代码并不相同。特别地,$list[] = [$i => [$i]] 应该改为 $list[$i] = [$i] 或者 $list[] = [$i] - NikiC
在你的基准代码中,你在哪里应用了数组并集? - revo
1
@NikiC 啊...你说得对!!! 我太专注于PHP内部,不小心忽略了这个错误。这就解释了为什么我创建了一个带有过高索引($i)的数组而需要双倍的内存!好的,内存错误已经被修复了。现在它有意义了。我的UNION理论正确吗?您能在数组合并部分添加一些额外的解释吗?我添加了一些可能有用的ZEND链接。Revo,你会怎样称呼:"list[$i] = [$i]"? - Lukas Lukac
@EnchanterIO 这里 array_merge 和 []= 的关键区别在于,array_merge 复制数组而 []= 不复制(除非需要增加容量)。这就是为什么 array_merge 是 O(n) 而 []= 是 (分摊) O(1)。循环运行时,这是 O(n^2) 和 O(n) 之间的差异。在那一点上,具体的实现细节就不再重要了。 - NikiC
实际上在进一步检查后,我意识到我的先前基准测试结果是误导性的,并且在某些真实场景下几乎没有用处。@NikiC,请查看我的更多详细答案和第二个基准测试结果。 - Lukas Lukac
1个回答

1
我认为我的基准测试不公平,因为(希望没有常识的人)会循环遍历数组,然后使用array_merge合并一个元素。
我创建了一个新的基准测试,展示了使用foreach和手动添加与使用array_merge将2个大数组合并成一个的区别。

enter image description here

结果

手动合并两个数组与使用 array_merge 合并它们没有性能优势。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接