Press "Enter" to skip to content

如何写出最快的循环

你知道怎么写出最快的循环么?
刚刚在晓东郭的blog看到一个有趣的问题" PHP中 $i++ 和 ++$i 的区别 ":

方式一:
$begin = time();
$i = 0;
while(++$i < 10000)
{
  $j = 0;
  while(++$j < 10000)
    ;
  ;
}
$end = time();
时间 : 16s
方式二:
$begin = time();
$i = 0;
while($i < 10000)
{
  $j = 0;
  while($j < 10000)
    ++$j;
  ++$i;
}
$end = time();
时间:13s
方式三:
$begin = time();
$i = 0;
while($i < 10000)
{
  $j = 0;
  while($j < 10000)
    $j++;
  $i++;
}
$end = time();
时间:15s
方式四:
$begin = time();
$i = 0;
while($i++ < 10000)
{
  $j = 0;
  while($j++ < 10000)
    ;
  ;
}
$end = time();
时间:13s

呵呵, 为什么会这样呢?
对比第一种方法和第二种方法, 因为在PHP中, 最终被执行的是OPCODE, 每行opline都有俩个操作数, 对于操作数来说, 一般有3种类型的存取方式, 临时变量, 变量, 和编译时变量, 这三种变量其中, 存取最快的是第三种, 编译器变量, 在OpCode执行过程中, 会讲一个变量的加一级引用存储在一个hash结构中, 用来加快存取速度.
在第一种方法中:

$i = 0;
while(++$i < 10000)
{
  $j = 0;
  while(++$j < 10000)
    ;
  ;
}

因为对于++$i来说, 我们需要得到它的返回值, 来和10000做比较, 这样就会使得PHP在编译的时候 , 生成一个变量(IS_VAR), 来保存自增的结果 , 也就是说, 这个时候用到了opline的return操作数.
然后, PHP会拿这个变量(IS_VAR)来和10000做比较.
而对于第二种方式:

$i = 0;
while($i < 10000)
{
  $j = 0;
  while($j < 10000)
    ++$j;
  ++$i;
}

这个过程中, $i已经优化成了编译变量(IS_CV), 而对于++$i, 因为我们不需要保存他的返回值, 所以也只是直接对编译变量进行自增..
也就是说, 方法一和方法二的速度差异, 就在于 对于方式二, 我们一直都在实用编译变量.. 编译变量的存取速度远快于变量(IS_VAR)
再来看第三种和第四种方式:

//3:
$i = 0;
while($i < 10000)
{
  $j = 0;
  while($j < 10000)
    $j++;
  $i++;
}
//4:
$i = 0;
while($i++ < 10000)
{
  $j = 0;
  while($j++ < 10000)
    ;
  ;
}

我们知道后缀自增(POST_INC), 会返回一个对原值的copy, 然后自增.
对于第四种方式, $i++以后, ZE会将$i的原值, 存储在一个临时变量(IS_TMP_VAR). 并且会拿这个临时变量和10000对比.
所以, 严格来讲, 这部分的速度比起第一种方式来说是会慢一些的.
而第三种方式慢在, 因为对$i++的返回值没有使用, 在语法分析阶段, $j++会归约成(rw_variable T_INC->expr_without_variable->expr, expr+';' => zend_do_free). 所以ZE就会安排一条opline, free掉这个临时变量....

23 Comments

  1. gump
    gump December 2, 2014

    第一种是不是循环的次数 和后面的不一样?版本5.5.18

  2. nange
    nange June 1, 2014

    哦,还真是第三个快啊,我在我的机子上跑这段代码,第一个用了5秒,第二个没试,直接式第三个,结果是用时0秒,看来还是需要弄懂PHP的工作原理呀。

  3. emerald_zj
    emerald_zj July 18, 2012

    我也同意用中文!!好容易找一个不是英文的站点

  4. tonado
    tonado April 25, 2009

    是5.2.8
    刚才说错了,第四种的POST_INC是有返回值存到临时变量里的,刚好用来比较,即
    第四种是:
    POST_INC !0 =>RES[~1]
    IS_SMALLER ~1, 1000 =>RES[~2]
    第二种是:
    IS_SMALLER !0, 1000 =>RES[~1]

    PRE_INC !0
    第一种是:
    PRE_INC !0 =>RES[$1]
    IS_SMALLER $1, 1000 =>RES[~2]
    四和二互有胜负就先不管了,理论上感觉二应该要比四快,但是我测了很多次结果第四种就没输过第一种,所有才有IS_TMP_VAR会不会比IS_VAR快的疑问,至于IS_CV,会不会它会做额外的工作,导致有时候反而不如一气呵成的IS_TMP_VAR呢?

    • 雪候鸟
      雪候鸟 April 26, 2009

      IS_TMP_VAR和IS_CV都是从execute_data结构中获取的.
      跟踪后发现,俩者的速度应该差别不是很大.都是一个类似数组结构的”cache”

  5. tonado
    tonado April 25, 2009

    刚才我测试了一下,测了5次,结果如下
    9.7706120014191
    9.0341799259186
    10.309438943863
    8.8307638168335
    9.7692799568176
    8.8732059001923
    10.170838117599
    8.8990998268127
    9.7464888095856
    8.8881361484528
    10.337028026581
    8.9553401470184
    9.7715139389038
    8.9210159778595
    10.189666032791
    8.8990459442139
    9.7989208698273
    9.0310311317444
    10.350558996201
    8.9781529903412
    结果显示,第四种方法应该是最快的(或者说第四种和第二种并列第一),并不像laruence所说的比第一种慢,这意味着IS_TEM_VAR要比IS_VAR快?好像这是唯一的解释了…让我意外的是第四种竟然大多数情况下比第二种还要快,既然二四的前/后自增都不需要返回值,假定POST_INC和PRE_INC性能一样的话,那么就是说使用!进行IS_SMALLER运算的第二种跟使用~的第四种半斤八两?甚至,大多数情况下还要差?

    • 雪候鸟
      雪候鸟 April 25, 2009

      哦? 从理论上来讲, 应该是IS_CV最快. 你的PHP版本是?

  6. TinTin
    TinTin April 19, 2009

    我在JAVA1.6下测试(不是JSP,不过应该差不多) JAVA要比PHP强悍,以亿次循环以毫秒为单位 说说测试结果吧(平均): 第一种:208MS 第二种:228MS 第三种:228MS 第四种:210MS

    • 雪候鸟
      雪候鸟 April 20, 2009

      恩, JAVA是预编译么. 性能来讲JSP确实要比PHP来的快些

      • TinTin
        TinTin April 20, 2009

        我觉得不是预编译的问题,因为随着循环次数的增加,PHP的消耗时间呈一次函数形式增长,如果存在预编译问题,哪么PHP循环一百次和JAVA循环时间应该差很大,但是PHP要快于JAVA
        对了,问个问题,PHP预编译的时间会不计算在$start_time=time();$end_time=time();之中?

        • 雪候鸟
          雪候鸟 April 20, 2009

          有理,是不会计算的.

  7. 网鬼
    网鬼 April 18, 2009

    还好一直用第二种

  8. 古嗣小井
    古嗣小井 April 18, 2009

    想请教个问题while的写法:
    $i = 0;
    while($i++ < 10000)
    {
    $j = 0;
    while($j++ < 10000)
    ;
    ;
    }
    一般我都是这样写的:
    $i = 0;
    while($i++ < 10000)
    {
    $j = 0;
    while($j++ < 10000){
    ;
    }
    ;
    }
    省掉第二个while的{}有什么特别的意义么?

  9. z.y.f
    z.y.f April 9, 2009

    学习……

  10. anders
    anders April 7, 2009

    哈哈,Thanks.

    • 雪候鸟
      雪候鸟 April 8, 2009

      @明城, 恩, 本文的重点在于PHP特别性. 🙂

  11. eve
    eve April 7, 2009

    看球不懂~

    • 雪候鸟
      雪候鸟 April 7, 2009

      @eve, …回家待着去….

    • unnamed
      unnamed April 8, 2009

      @eve, 还是很简单明了的

Comments are closed.