在 foreach 循环参数中分解数组
foreach(explode(',' $foo) as $bar) { ... }
对比
$test = explode(',' $foo);
foreach($test as $bar) { ... }
在第一个例子中,它是在每次迭代时explode
$foo
字符串还是PHP 将它保存在内存中并在它自己的临时变量中爆炸?从效率的角度来看,创建额外的变量 $test
是否有意义,或者两者几乎相等?
In the first example, does it explode
the $foo
string for each iteration or does PHP keep it in memory exploded in its own temporary variable? From an efficiency point of view, does it make sense to create the extra variable $test
or are both pretty much equal?
推荐答案
我可以做出有根据的猜测,但让我们尝试一下!
I could make an educated guess, but let's try it out!
我认为有三种主要方法可以解决这个问题.
I figured there were three main ways to approach this.
- 在进入循环之前爆炸并赋值
- 在循环中爆炸,没有赋值
- 字符串标记化
我的假设:
- 可能由于分配而消耗更多内存
- 可能与 #1 或 #3 相同,不确定哪个
- 可能更快,内存占用也小得多
方法
这是我的测试脚本:
Approach
Here's my test script:
<?php
ini_set('memory_limit', '1024M');
$listStr = 'text';
$listStr .= str_repeat(',text', 9999999);
$timeStart = microtime(true);
/*****
* {INSERT LOOP HERE}
*/
$timeEnd = microtime(true);
$timeElapsed = $timeEnd - $timeStart;
printf("Memory used: %s kB
", memory_get_peak_usage()/1024);
printf("Total time: %s s
", $timeElapsed);
这里是三个版本:
1)
// explode separately
$arr = explode(',', $listStr);
foreach ($arr as $val) {}
2)
// explode inline-ly
foreach (explode(',', $listStr) as $val) {}
3)
// tokenize
$tok = strtok($listStr, ',');
while ($tok = strtok(',')) {}
结果
看起来有些假设被推翻了.你不爱科学吗?:-)
Looks like some assumptions were disproven. Don't you love science? :-)
- 总的来说,这些方法中的任何一种对于合理大小"(几百或几千)的列表来说都足够快.
- 如果您要迭代巨大的内容,时间差异相对较小,但内存使用量可能会相差一个数量级!
- 当你
explode()
在没有预赋值的情况下内联时,由于某种原因它会慢一点. - 令人惊讶的是,标记化比显式迭代声明的数组慢.在如此小的规模上工作,我相信这是由于每次迭代对
strtok()
进行函数调用的调用堆栈开销.更多内容请参见下文.
- In the big picture, any of these methods is sufficiently fast for a list of "reasonable size" (few hundred or few thousand).
- If you're iterating over something huge, time difference is relatively minor but memory usage could be different by an order of magnitude!
- When you
explode()
inline without pre-assignment, it's a fair bit slower for some reason. - Surprisingly, tokenizing is a bit slower than explicitly iterating a declared array. Working on such a small scale, I believe that's due to the call stack overhead of making a function call to
strtok()
every iteration. More on this below.
就函数调用的数量而言,explode()
ing 确实是标记化的佼佼者.O(1) vs O(n)
In terms of number of function calls, explode()
ing really tops tokenizing. O(1) vs O(n)
我在运行方法 1) 的图表中添加了一个奖励,并在循环中调用了一个函数.我使用了 strlen($val)
,认为这将是一个相对相似的执行时间.这是有争议的,但我只是想提出一个一般性的观点.(我只运行了 strlen($val)
并忽略了它的输出.我没有没有将它分配给任何东西,因为分配会增加额外的时间成本.)>
I added a bonus to the chart where I run method 1) with a function call in the loop. I used strlen($val)
, thinking it would be a relatively similar execution time. That's subject to debate, but I was only trying to make a general point. (I only ran strlen($val)
and ignored its output. I did not assign it to anything, for an assignment would be an additional time-cost.)
// explode separately
$arr = explode(',', $listStr);
foreach ($arr as $val) {strlen($val);}
正如您从结果表中看到的那样,它成为三种方法中最慢的方法.
As you can see from the results table, it then becomes the slowest method of the three.
这很有趣,但我的建议是做任何你认为最易读/最可维护的事情.只有当您真正处理非常大的数据集时,您才应该担心这些微优化.
This is interesting to know, but my suggestion is to do whatever you feel is most readable/maintainable. Only if you're really dealing with a significantly large dataset should you be worried about these micro-optimizations.
相关文章