优化值得的时间?
我是一名PHP开发人员,我一直认为微型优化不值得。 如果你真的需要额外的性能,你可以编写你的软件,以便体系结构更快,或者编写一个C ++扩展来处理缓慢的任务(或者更好的是,使用HipHop编译代码)。 然而,今天的一位同事告诉我说,这有很大的不同
is_array($array)
和
$array === (array) $array
我真的很喜欢“呃,这实在是毫无意义的比较”,但他不同意我的观点。他是我们公司最好的开发人员,并负责一个每天处理大约5000万SQL查询的网站 - - 例如。 所以,我在这里想知道他可能是错的还是微观优化真的值得时间和地点?
当你有证据证明你正在优化一个瓶颈时,微型优化是值得的。
通常这是不值得的 - 编写最可读的代码,并使用现实的基准来检查性能。 如果当你发现你有瓶颈时,微调优化这些代码(按照你的要求测量)。 有时候少量的微观优化可以产生巨大的差异。
但是不要微观地优化所有的代码......它最终将难以维护,而且你很可能会发现你或者错过了真正的瓶颈,或者你的微观优化损害了性能,而不是帮助。
那么,对于一个普通的小数组, $array === (array) $array
明显比is_array($array)
快。 速度提高了7倍以上。 但是每次通话只需要1.0 x 10 ^ -6
秒( 0.000001 seconds
)。 所以,除非你几千次这样称呼,否则这不值得。 如果你打电话给它几千次,我会建议你做错了什么...
当你处理一个大数组时,区别就在于此。 由于$array === (array) $array
需要复制一个新变量,因此需要将该数组在内部进行迭代以进行比较,所以对于大型数组而言,它可能会显着较慢。 例如,具有100个整数元素的数组上, is_array($array)
是错误(余量范围内< 2%
的) is_array()
具有小的阵列(在未来在0.0909
秒万次迭代)。 但$array = (array) $array
非常慢。 只有100个元素,它已经比is_array()
慢了两倍(进入0.203
秒)。 对于1000个元素, is_array
保持不变,但演员比较增加到2.0699
秒...
小数组的速度更快的原因是, is_array()
具有作为函数调用的开销,其中cast操作是一种简单的语言结构...并且遍历一个小变量(使用C代码)通常会比函数调用开销。 但是,对于较大的变量,差异会增大......
这是一个折衷。 如果数组足够小,迭代效率会更高。 但随着数组大小的增长,它会变得越来越慢(因此函数调用会变得更快)。
另一种方式来看待它
另一种看待它的方法是检查每个演员的算法复杂性。
先来看看is_array()
。 它的源代码基本上显示它是一个O(1)
操作。 这意味着它是一个恒定的时间操作。 但我们也需要看看函数调用。 在PHP中,具有单个数组参数的函数调用是O(1)
或O(n)
具体取决于是否需要触发写入时复制。 如果在$array
是变量引用时调用is_array($array)
,则将触发写时复制,并且会发生变量的完整副本。
因此, is_array()
是最好的情况O(1)
和最坏的情况O(n)
。 但只要你不使用引用,它总是O(1)
...
另一方面,演员版则执行两项操作。 它做了一个演员,然后它进行了平等检查。 所以让我们分别看看。 转换操作符处理程序首先强制输入变量的副本。 不管它是否是参考。 所以简单地使用(array)
操作符强制对数组进行O(n)
迭代来转换它(通过copy_ctor调用)。
然后,它将新副本转换为数组。 对于数组和基元,这是O(1)
,但对于O(n)
。
然后,执行相同的操作符。 处理程序只是is_identical_function()
的代理。 现在,如果$array
不是数组,is_identical将会短路。 因此,它具有O(1)
的最佳情况。 但是如果$array
是一个数组,那么如果哈希表是相同的(这意味着两个变量都是相互拷贝的拷贝),那么它可以再次短路。 所以这种情况也是O(1)
。 但请记住,我们强制上面的副本,所以如果它是一个数组我们不能这样做。 所以这是O(n)
感谢zend_hash_compare ...
所以最终的结果就是这个最糟糕的运行时间表:
+----------+-------+-----------+-----------+---------------+
| | array | array+ref | non-array | non-array+ref |
+----------+-------+-----------+-----------+---------------+
| is_array | O(1) | O(n) | O(1) | O(n) |
+----------+-------+-----------+-----------+---------------+
| (array) | O(n) | O(n) | O(n) | O(n) |
+----------+-------+-----------+-----------+---------------+
请注意,它们看起来像是对参考进行缩放。 他们不。 它们都对参考变量进行线性缩放。 但恒定的因素变化。 例如,在大小为5的引用数组中,is_array将执行5个内存分配和5个内存副本,然后是1个类型检查。 另一方面,演员版本将执行5个内存分配,5个内存副本,接着是2个类型检查,随后是5个类型检查和5个相等检查( memcmp()
等)。 因此, n=5
为is_array
生成11个操作数,而在===(array)
则为22个操作数...
现在, is_array()
确实具有堆栈推送的O(1)开销(由于函数调用),但这只会在运行时支配非常小的n
值(我们在基准测试中看到只有10个数组元素是足以完全消除所有差异)。
底线
尽管如此,我还是建议去阅读。 我发现is_array($array)
比$array === (array) $array
更具可读性。 所以你得到两全其美。
我用于基准的脚本:
$elements = 1000;
$iterations = 10000;
$array = array();
for ($i = 0; $i < $elements; $i++) $array[] = $i;
$s = microtime(true);
for ($i = 0; $i < $iterations; $i++) is_array($array);
$e = microtime(true);
echo "is_array completed in " . ($e - $s) ." Secondsn";
$s = microtime(true);
for ($i = 0; $i < $iterations; $i++) $array === (array) $array;
$e = microtime(true);
echo "Cast completed in " . ($e - $s) ." Secondsn";
编辑:为了记录,这些结果与Linux上的5.3.2 ...
编辑2:修正了数组较慢的原因(这是由于迭代比较而不是内存原因)。 请参阅compare_function以了解迭代代码...
微型优化是值得的时间?
不,除非是。
换句话说,先验的答案是“不”,但是在知道特定的代码行消耗了健康的时钟时间之后,那么只有这样才值得优化。
换句话说,首先要知道,因为否则你就没有这种知识。 无论语言还是操作系统,这都是我依赖的方法。
补充说:当很多程序员讨论性能时,从专家的角度来看,他们倾向于谈论程序花费时间在哪里。 在“哪里”导致他们远离可以节省大部分时间的东西,即函数调用站点,这有一种模糊的含糊不清的含义。 毕竟,应用程序顶部的“调用Main”是一个“地点”,该程序几乎从不“在”,但是需要100%的时间负责。 现在你不会摆脱“打电话给主”,但几乎总是有其他电话可以摆脱。 当程序打开或关闭文件,或将一些数据格式化为一行文本,或等待套接字连接,或者“新建”一块内存,或者在整个大型数据结构中传递通知时,它是花费大量的时间来调用函数,但它是“在哪里”? 无论如何,这些调用很快就会在堆栈样本中找到。
链接地址: http://www.djcxy.com/p/40367.html