额外的变量或对象创建 - 在PHP中更好


Extra variable or object creation -- what is better in PHP?

PHP中什么更好/更快/更有效?

使用额外变量:

$dateStep = new DateInterval('P1D');
for($di = 0; $di <= $dateInterval; ++$di)
{
    $theDate = ($di > 0) ? $startDate->add($dateStep) : $startDate;
    ...
}

或者每次需要时创建对象:

for($di = 0; $di <= $dateInterval; ++$di)
{
    $theDate = ($di > 0) ? $startDate->add(new DateInterval('P1D')) : $startDate;
    ...
}

我会投票支持第一个选项,但我在 PHP 优化和性能方面的知识非常有限。

编辑:看起来,我表达得不够,但我特别要求这样的场景 - 当对象的创建返回静态值时,在后续循环中一遍又一遍地使用。

为了从另一个角度来看这个问题,让我们看一下脚本和操作码:

脚本 1:

<?php
$dateStep = new DateInterval('P1D');
$dateInterval = 5;
$startDate = new DateTime();
for($di = 0; $di <= $dateInterval; ++$di)
{
    $theDate = ($di > 0) ? $startDate->add($dateStep) : $startDate;
    //...
}

操作码:

number of ops:  26
compiled vars:  !0 = $dateStep, !1 = $dateInterval, !2 = $startDate, !3 = $di, !4 = $theDate
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   3     0  >   FETCH_CLASS                                   4  :0      'DateInterval'
         1      NEW                                              $1      :0
         2      SEND_VAL                                                 'P1D'
         3      DO_FCALL_BY_NAME                              1          
         4      ASSIGN                                                   !0, $1
   4     5      ASSIGN                                                   !1, 5
   5     6      FETCH_CLASS                                   4  :5      'DateTime'
         7      NEW                                              $6      :5
         8      DO_FCALL_BY_NAME                              0          
         9      ASSIGN                                                   !2, $6
   7    10      ASSIGN                                                   !3, 0
        11  >   IS_SMALLER_OR_EQUAL                              ~10     !3, !1
        12    > JMPZNZ                                        F          ~10, ->25
        13  >   PRE_INC                                                  !3
        14    > JMP                                                      ->11
   9    15  >   IS_SMALLER                                       ~12     0, !3
        16    > JMPZ                                                     ~12, ->22
        17  >   INIT_METHOD_CALL                                         !2, 'add'
        18      SEND_VAR                                                 !0
        19      DO_FCALL_BY_NAME                              1  $14     
        20      QM_ASSIGN_VAR                                    $15     $14
        21    > JMP                                                      ->23
        22  >   QM_ASSIGN_VAR                                    $15     !2
        23  >   ASSIGN                                                   !4, $15
  12    24    > JMP                                                      ->13
        25  > > RETURN                                                   1

脚本 2:

<?php

$dateInterval = 5;
$startDate = new DateTime();
for($di = 0; $di <= $dateInterval; ++$di)
{
    $theDate = ($di > 0) ? $startDate->add(new DateInterval('P1D')) : $startDate;
   // ...
}

操作码:

number of ops:  25
compiled vars:  !0 = $dateInterval, !1 = $startDate, !2 = $di, !3 = $theDate
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   4     0  >   ASSIGN                                                   !0, 5
   5     1      FETCH_CLASS                                   4  :1      'DateTime'
         2      NEW                                              $2      :1
         3      DO_FCALL_BY_NAME                              0          
         4      ASSIGN                                                   !1, $2
   7     5      ASSIGN                                                   !2, 0
         6  >   IS_SMALLER_OR_EQUAL                              ~6      !2, !0
         7    > JMPZNZ                                        A          ~6, ->24
         8  >   PRE_INC                                                  !2
         9    > JMP                                                      ->6
   9    10  >   IS_SMALLER                                       ~8      0, !2
        11    > JMPZ                                                     ~8, ->21
        12  >   INIT_METHOD_CALL                                         !1, 'add'
        13      FETCH_CLASS                                   4  :10     'DateInterval'
        14      NEW                                              $11     :10
        15      SEND_VAL                                                 'P1D'
        16      DO_FCALL_BY_NAME                              1          
        17      SEND_VAR_NO_REF                               0          $11
        18      DO_FCALL_BY_NAME                              1  $13     
        19      QM_ASSIGN_VAR                                    $14     $13
        20    > JMP                                                      ->22
        21  >   QM_ASSIGN_VAR                                    $14     !1
        22  >   ASSIGN                                                   !3, $14
  12    23    > JMP                                                      ->8
        24  > > RETURN                                                   1

现在,正如您在第二个脚本的操作码中看到的那样,它每次迭代都会创建类的新实例,并且您会产生巨大的开销,这似乎不需要(查看第二个操作码中的# * 13 - 16),因此首先您正在创建新实例,这是完全不必要的,其次这会降低您的性能。

因此,只要您不需要每次迭代都使用一个全新的类,第一个脚本对您来说更好,因为它只创建一次DateInterval对象并将其分配给变量。

还有一点额外的信息:

  • 示例 1
  • 示例 2
  • VLD 扩展
  • ZE2 操作码

我将采用以下内容

$dateStep = new DateInterval('P1D');
for($di = 0; $di > $dateInterval; ++$di)
{
    $theDate = ($di > 0) $startDate->add($dateStep);
    ...
}

除非确实有必要,否则不建议在循环中一遍又一遍地创建对象。在上述情况下,对象是在循环之前创建的,并且传递对象以节省时间(用于创建另一个对象)和空间(通过在内存中不创建另一个对象)。

两个版本在逻辑上是不同的。

第一个版本多次传递相同的对象引用。这意味着如果您在循环后更改$dateStep则会更改所有引用。

第二个版本在每次调用该方法时都会应用对单个对象的引用。


在使用 DateTime::add() 方法的示例中,$dateStep可以看作是不会更改的常量值。在这种情况下,第一个版本更快,更可取,因为它不需要每次都调用构造函数,并且它确实需要更少的内存,因为对象只需要创建一次。

肯定第一个更快。N 类实例比 1 个变量声明更昂贵。