猿问

为什么用 min() 和 max() 比较数字比条件语句慢

我不得不优化运行缓慢的大型数据集的代码,这些代码看起来不错。在测试和记录执行时间后,我发现循环中几乎没有两个整数的 max() 比较。然后在快速浏览文档后,我看到了以下评论:“我有几次使用 max 比使用 if/then/else 构造要慢得多。一定要在你的例程中检查这个!”


所以我用三元运算符替换它,执行时间快了大约 2/3。


然后我开始好奇为什么 max() 的工作速度比一个简单的语句慢得多,以及这个函数的背后是什么。但到目前为止我找不到任何解释。


同样在过去,我在 array_search() 中遇到了类似的问题,它的工作速度比 foreach with 语句慢得多。


我在 PHP 7.1 上运行了测试。


$firstLoop = microtime(true);


for ($i = 0; $i < 1000000; $i++) {

    $testOne = max($i, 1000000);

}


error_log(microtime(true) - $firstLoop); 


$secondLoop = microtime(true); // 1.3123650550842


for ($i = 0; $i < 1000000; $i++) {

    $testTwo = $i > 1000000 ? $i : 1000000; 

}


error_log(microtime(true) - $secondLoop); // 0.090374946594238


qq_遁去的一_1
浏览 296回答 3
3回答

海绵宝宝撒

max是一个完整的函数调用并允许任意参数列表。与可以在不设置完整函数调用上下文的情况下内联执行的简单比较相比,这有很多开销。在您的示例中,您正在执行一百万次函数调用,这总共加起来。两种不同的方法(支持(并且可以做)不同的事情)没有必要以相同的方式实现,即使它们给出与您的示例相同的结果。您可以从 VM 生成的操作码中看到这一点,第一次调用生成以下语句:&nbsp; &nbsp;6&nbsp; &nbsp; &nbsp;6&nbsp; &nbsp; >&nbsp; &nbsp;INIT_FCALL&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'max'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;7&nbsp; &nbsp; &nbsp; &nbsp; SEND_VAR&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;!1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;8&nbsp; &nbsp; &nbsp; &nbsp; SEND_VAL&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;9&nbsp; &nbsp; &nbsp; &nbsp; DO_ICALL&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$8&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 10&nbsp; &nbsp; &nbsp; &nbsp; ASSIGN&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;!2, $8&nbsp; &nbsp;5&nbsp; &nbsp; 11&nbsp; &nbsp; &nbsp; &nbsp; POST_INC&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;~10&nbsp; &nbsp; &nbsp;!1&nbsp; &nbsp; &nbsp; &nbsp; 12&nbsp; &nbsp; &nbsp; &nbsp; FREE&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;~10&nbsp; &nbsp; &nbsp; &nbsp; 13&nbsp; &nbsp; >&nbsp; &nbsp;IS_SMALLER&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;~11&nbsp; &nbsp; &nbsp;!1, 1000000而第二种情况只生成 JMP 调用:&nbsp; &nbsp; &nbsp; &nbsp; 27&nbsp; &nbsp; &nbsp; > JMP&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ->36&nbsp; 14&nbsp; &nbsp; 28&nbsp; &nbsp; >&nbsp; &nbsp;IS_SMALLER&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;~18&nbsp; &nbsp; &nbsp;1000000, !1&nbsp; &nbsp; &nbsp; &nbsp; 29&nbsp; &nbsp; &nbsp; > JMPZ&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;~18, ->32&nbsp; &nbsp; &nbsp; &nbsp; 30&nbsp; &nbsp; >&nbsp; &nbsp;QM_ASSIGN&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ~19&nbsp; &nbsp; &nbsp;!1&nbsp; &nbsp; &nbsp; &nbsp; 31&nbsp; &nbsp; &nbsp; > JMP&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ->33&nbsp; &nbsp; &nbsp; &nbsp; 32&nbsp; &nbsp; >&nbsp; &nbsp;QM_ASSIGN&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ~19&nbsp; &nbsp; &nbsp;1000000&nbsp; &nbsp; &nbsp; &nbsp; 33&nbsp; &nbsp; >&nbsp; &nbsp;ASSIGN&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;!4, ~19&nbsp; 13&nbsp; &nbsp; 34&nbsp; &nbsp; &nbsp; &nbsp; POST_INC&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;~21&nbsp; &nbsp; &nbsp;!1&nbsp; &nbsp; &nbsp; &nbsp; 35&nbsp; &nbsp; &nbsp; &nbsp; FREE&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;~21&nbsp; &nbsp; &nbsp; &nbsp; 36&nbsp; &nbsp; >&nbsp; &nbsp;IS_SMALLER&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;~22&nbsp; &nbsp; &nbsp;!1, 1000000JMP call 不必设置整个函数调用结构,因此可以更快地执行。这也是一文不值的是,在一般的差异似乎是大约1/2的时间用于对PHP的当前版本的简单对比。

交互式爱情

在以前的 PHP 版本中,它比这慢得多。他们正在提高每个库函数的速度,如新版本中的 max() 和 min() 函数。正如 MatsLindh 在 max() 和 min() 等库函数中所说的那样,它们有很多开销,我们在第二部分中所做的简单比较中不需要这些开销。如果您阅读PHP 手册 max()它说...如果第一个也是唯一的参数是数组,则 max() 返回该数组中的最大值。如果至少提供了两个参数,则 max() 返回这些值中的最大值。所以它首先检查它是否是一个数组,这需要一些执行时间。在您的情况下,您只是在比较值而不是检查数组。将使用标准比较规则比较不同类型的值。例如,一个非数字字符串将与一个整数进行比较,就好像它是 0 一样,但多个非数字字符串值将按字母数字进行比较。返回的实际值将是未应用转换的原始类型。所以在 max 函数中有更多的函数需要一些时间来执行。除此之外,该功能比简单的比较需要更多的时间。这就是为什么像 Lisp 这样基于函数的编程语言比一个像样的哈希表慢的原因。他们需要的基础设施不可避免地增加了理论上使用手工汇编程序可以实现的开销。特别是,一流的词法闭包只适用于垃圾收集,因为它们允许值超出范围。可能是当今函数式语言的最大瓶颈:过度的分配率。

天涯尽头无女友

确保您使用多次,然后将它们平均,然后决定哪些代码运行得更快。在我的机器上,使用 max 函数的代码平均(10 个结果)为0.08,if 语句为0.044&nbsp;我们可以看到 if 条件花费的时间几乎是 min 函数的一半。如果我们考虑调用函数时会发生什么,那么这个时间差是有意义的。1) 函数的参数存储在堆栈中。按平台特定顺序。2)返回值的位置在堆栈上“分配”3) 函数的返回地址也存储在堆栈或专用 CPU 寄存器中。4) 函数(或者实际上是函数的地址)被调用,要么通过 CPU 特定的调用指令,要么通过普通的 jmp 或 br 指令(跳转/分支)5) 函数从堆栈中读取参数(如果有)并运行函数代码6) 函数返回值存放在指定位置(堆栈或CPU专用寄存器)7) 执行跳回到调用者处并清空堆栈(通过将堆栈指针恢复到其初始值)。访问调用方法/函数时汇编语言中会发生什么?如果我们考虑到所有这些细节以及函数内部增加的代码行来处理多个条件来搜索最大值,那么时间差是有道理的。访问https://www.php.net/manual/en/function.max.php查看函数处理的增加代码行数从而增加执行时间的条件。对 array_search 和 min 函数的类似响应。
随时随地看视频慕课网APP
我要回答