微优化值得花时间吗?

12 浏览
0 Comments

微优化值得花时间吗?

作为一名PHP开发者,我一直认为微优化不值得花时间去做。如果你真的需要额外的性能,你可以通过设计更高效的软件架构,或者编写C++扩展来处理慢速任务(或者更好的是使用HipHop编译代码)。然而,今天我的一个同事告诉我,在is_array($array)$array === (array) $array之间有很大的区别,我感觉这是一个毫无意义的比较,但他不同意我的观点...而他是我们公司最优秀的开发者,负责每天处理大约5000万个SQL查询的网站。所以,我在想:他可能错了吗?微优化真的值得花时间去做吗?何时值得呢?

0
0 Comments

微观优化是否值得时间?

不值得,除非是值得的。

换句话说,事先的答案是“不”,但只有在你知道特定的代码行消耗了很大的时钟时间之后,才值得进行优化。

换句话说,首先进行性能分析,因为否则你就没有这个知识。无论使用什么语言或操作系统,这是我依赖的方法

补充:当许多程序员讨论性能时,从专家到初学者,他们往往会谈论程序花费时间的“位置”。这个“位置”中有一个隐蔽的模糊性,它让他们远离可能节省最多时间的事物,也就是函数调用点。毕竟,在应用程序顶部的“call Main”几乎从来不是程序所在的“位置”,但却负责了100%的时间。现在你无法摆脱“call Main”,但几乎总会有其他你可以摆脱的调用。

当程序打开或关闭文件、将某些数据格式化为一行文本、等待套接字连接、创建一块内存或在一个大型数据结构中传递通知时,它在调用函数中花费了大量的时间,但那是它所在的“位置”吗?无论如何,这些调用可以通过堆栈采样快速找到。

关于“通过堆栈采样快速找到”:是的,但很少有人知道,尽管它非常简单(也许这就是问题所在)。例如,通常可以在Visual Studio中完成,而无需其他工具。

:这是一个教育问题。40年前,我是一名教授,我亲眼见证了这个问题。教授们很少接触真正庞大的代码-他们只关注算法和大O符号,这在某种程度上是可以接受的。当课程提到“谈论性能分析”时,他们会谈论gprof及其现代版本。完成这个任务后,他们就会继续下一个任务。学生们会做什么呢?在课堂上,他们的思维是开放的,可以接受智慧的灌输。然后当他们毕业时,他们认为角色发生了逆转-他们的工作是教育我们这些古董他们所学到的东西。我相信你也见过这种情况。 🙂

0
0 Comments

微优化是否值得花时间?

当你有证据表明你正在优化一个瓶颈时,微优化是值得的。

通常情况下,这是不值得的-编写尽可能易读的代码,并使用实际的基准测试来检查性能。如果你发现自己有一个瓶颈,只需对那部分代码进行微优化(同时进行测量)。有时微小的优化可以带来巨大的差异。

但是不要对所有代码进行微优化...这样会更难维护,并且你很可能会发现你要么错过了真正的瓶颈,要么你的微优化会损害性能而不是帮助。

完全同意。这值得花时间吗?你可以通过分析来了解你的应用程序,从而确定优化会带来足够的好处。回答另一个问题,“你应该对所有代码进行微优化吗?”绝对不。在大多数情况下,清晰易读且易于维护的代码比快速但不敏感于性能的代码更重要。编程是关于妥协和平衡的。代码复杂性、可维护性、性能、程序员时间成本、硬件要求成本。大多数情况下,开发时间和错误比硬件成本更昂贵。

- 你能回到写关于C#等的书,让我们这些凡人来做低级别的工作吗?

:如果这能安慰你,我目前正在审阅《C# in Depth》的第14章的证据。SO只是偶尔的干扰 🙂

哦不,他也准备获得PHP徽章!

- 你把SO声望205K称为偶尔的干扰吗???但我期待着阅读你的下一本书。《C# in Depth》的原版让我想起了Scott Meyers的C++书籍,我非常喜欢。

:如果你在书中提到了性能分析,我希望你能考虑到这些问题:stackoverflow.com/questions/1777556/…

:不,关于性能分析,我在书中几乎没有提到。

0
0 Comments

微优化是否值得时间?

微优化是指对代码进行微小的改进,以提升性能。本文讨论了一个微优化的案例,即在判断一个变量是否为数组时,使用$array === (array) $array比使用is_array($array)更快。作者指出,对于一个非常小的数组来说,前者比后者快7倍以上。但每个调用的时间只有1.0 x 10 ^ -6秒(0.000001秒),所以除非需要调用非常多次,否则不值得进行微优化。对于大数组而言,情况就不同了。因为$array === (array) $array需要复制一个新变量,并且需要在比较时对数组进行迭代,所以对于大数组来说,它会明显慢得多。举例来说,对于包含100个整数元素的数组,is_array($array)和小数组时的is_array()相差不大(误差小于2%),耗时为0.0909秒(10,000次迭代)。但$array = (array) $array非常慢,对于只有100个元素的数组来说,它已经比is_array()慢两倍以上(耗时为0.203秒)。对于1,000个元素的数组来说,is_array的耗时保持不变,而强制转换的比较时间增加到了2.0699秒。

这个微优化之所以对小数组有效,是因为is_array()调用的开销比较大,而(array)转换操作是一个简单的语言结构。在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)的迭代(通过复制构造函数调用)。

然后,它将新的副本转换为数组。对于数组和基本类型来说,这是O(1),但对于对象来说,它是O(n)

然后,执行相等性运算符。这个操作只是一个代理到is_identical_function()的操作。现在,如果$array不是一个数组,is_identical会进行短路。因此,它的最优情况是O(1)。但如果$array是一个数组,它可以再次进行短路,如果哈希表是相同的(意味着两个变量都是彼此的写时复制副本)。所以这种情况也是O(1)。但请记住,我们在上面强制复制了一个副本,所以如果它是一个数组,我们就不能这样做。所以由于zend_hash_compare,它是O(n)...

因此,最终的结果是最坏情况下的运行时间表:

+----------+-------+-----------+-----------+---------------+
|          | 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($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) ." Seconds\n";
$s = microtime(true);
for ($i = 0; $i < $iterations; $i++) $array === (array) $array;
$e = microtime(true);
echo "Cast completed in " . ($e - $s) ." Seconds\n";

这些结果是在Linux上使用5.3.2版本得出的。

编辑:为了记录,这些结果是在Linux上使用5.3.2版本得到的。

编辑2:修正了数组比较较慢的原因(这是由于迭代比较而不是内存原因)。可以在compare_function中查看迭代代码...

最后,作者建议选择可读性更好的方法。他认为is_array($array)$array === (array) $array更易读。所以你可以兼顾两者。

0