我可以做出有根据的猜测,但让我们试试看!
我认为有三种主要方法可以解决这个问题。
- 在进入循环之前分解和赋值
- 在循环内爆炸,没有赋值
- 字符串分词
我的假设:
- 可能由于分配而消耗更多内存
- 可能与 #1 或 #3 相同,不确定是哪个
- 可能更快、更小内存占用
方法
这是我的测试脚本:
<?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\n", memory_get_peak_usage()/1024);
printf("Total time: %s s\n", $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(',')) {}
结果
结论
看起来有些假设被推翻了。你不爱科学吗? :-)
- 总体而言,这些方法中的任何一种都足够快,可以生成“合理大小”(几百或几千)的列表。
- 如果您正在迭代巨大的东西,时间差异相对较小,但内存使用量可能会相差一个数量级!
- 当您
explode() 在没有预先分配的情况下内联时,由于某种原因,它会慢一些。
- 令人惊讶的是,标记化比显式迭代声明的数组要慢一点。在如此小的规模上工作,我相信这是由于每次迭代都对
strtok() 进行函数调用的调用堆栈开销。详情请参阅下文。
就函数调用的数量而言,explode()ing 确实在标记化方面名列前茅。 O(1) vs O(n)
我在运行方法 1) 的图表中添加了一个奖励,并在循环中调用了一个函数。我使用了strlen($val),认为这将是一个相对相似的执行时间。这是有争议的,但我只是想提出一个一般性的观点。 (我只运行了strlen($val) 并忽略了它的输出。我确实没有将它分配给任何东西,因为分配会增加时间成本。)
// explode separately
$arr = explode(',', $listStr);
foreach ($arr as $val) {strlen($val);}
从结果表中可以看出,它成为三种方法中最慢的方法。
最后的想法
这很有趣,但我的建议是做任何你认为最易读/可维护的事情。只有当你真的在处理一个非常大的数据集时,你才应该担心这些微优化。