【问题标题】:Speed difference in using inline strings vs concatenation in php5?在php5中使用内联字符串与连接的速度差异?
【发布时间】:2010-09-06 00:44:30
【问题描述】:

(假设php5)考虑

<?php

    $foo = 'some words';

    //case 1
    print "these are $foo";

    //case 2
    print "these are {$foo}";

    //case 3
    print 'these are ' . $foo;
?>

1 和 2 有很大区别吗?

如果不是,那么 1/2 到 3 之间呢?

【问题讨论】:

  • 请注意,echo 'these are ', $foo; 比任何一个都快,因为没有连接或插值。
  • 为什么这个问题没有建设性?
  • 不知道。这实际上是一个种子问题,在网站启动后不久添加,当时鼓励测试人员发布早期谷歌搜索中会出现的基线问题,即使这些问题过于简单,或者接近于非-问题形式。鉴于 cmets 的观点和活动以及其中的投票,我认为这是非常有建设性的,恕我直言。
  • 请查看我对另一个问题的回答,该问题已在 cmets 中出现:stackoverflow.com/a/31312867/2893496
  • 在我的场景中单引号更快。我使用并行运行异步日志解析器,CPU 的性能提升让我有机会并行运行更多解析器。单引号我可以解析 144TB/小时双引号我可以解析少于 95TB。但是,只有当您已经完成了所有可以做的事情时,您才需要检查它 === 而不是 ==,字符串比较而不是正则表达式和大量其他操作。

标签: php optimization performance


【解决方案1】:

至少从 2012 年 1 月开始,性能差异一直是 irrelevant,可能更早:

Single quotes: 0.061846971511841 seconds
Double quotes: 0.061599016189575 seconds

早期版本的 PHP 可能有所不同——我个人更喜欢单引号而不是双引号,所以这是一个方便的区别。文章的结论提出了一个很好的观点:

永远不要相信不是你自己伪造的统计数据。

(虽然文章引用了这句话,但最初的俏皮话很可能是对温斯顿丘吉尔的错误attributed,这是约瑟夫戈培尔的宣传部发明的,将丘吉尔描绘成一个骗子:

Ich traue keiner Statistik, die ich nicht selbst gefälscht habe.

这大致翻译为“我不相信我没有伪造自己的统计数据。”)

【讨论】:

  • 这不只是编译时检查吗?
  • 而且,最重要的是,通过使用更少的像素,您可以减少温室气体排放。
  • 实际上,由于更快的计算意味着更少的 CPU 时间意味着更少的瓦特消耗,单引号确实可以减少温室气体排放。
  • @Paolo Begantino:你真的有任何证据吗? phpbench.com 每次加载时都恭敬地不同意你的看法。
  • 请注意,即使您使用单引号字符串,PHP 仍然会解析其中的每个字符,查找转义序列等。PHP 也会解析整个 文件 i>,所以充其量,您仍在查看 O(n) 的字符串长度。
【解决方案2】:

好吧,就像所有“现实生活中什么可能更快”的问题一样,你无法通过现实生活中的测试。

function timeFunc($function, $runs)
{
  $times = array();

  for ($i = 0; $i < $runs; $i++)
  {
    $time = microtime();
    call_user_func($function);
    $times[$i] = microtime() - $time;
  }

  return array_sum($times) / $runs;
}

function Method1()
{ 
  $foo = 'some words';
  for ($i = 0; $i < 10000; $i++)
    $t = "these are $foo";
}

function Method2()
{
  $foo = 'some words';
  for ($i = 0; $i < 10000; $i++)
    $t = "these are {$foo}";
}

function Method3()
 {
  $foo = 'some words';
  for ($i = 0; $i < 10000; $i++)
    $t = "these are " . $foo;
}

print timeFunc('Method1', 10) . "\n";
print timeFunc('Method2', 10) . "\n";
print timeFunc('Method3', 10) . "\n";

运行几次以将所有内容分页,然后...

0.0035568

0.0035388

0.0025394

因此,正如预期的那样,插值实际上是相同的(噪音水平差异,可能是由于插值引擎需要处理的额外字符)。直接串联大约是速度的 66%,这并没有太大的冲击。插值解析器会查找,发现无事可做,然后以简单的内部字符串 concat 结束。即使 concat 很昂贵,插值器仍然必须这样做,解析出变量并修剪/复制原始字符串的所有工作之后。

Somnath 更新:

我在上面的实时逻辑中添加了 Method4()。

function Method4()
 {
  $foo = 'some words';
  for ($i = 0; $i < 10000; $i++)
    $t = 'these are ' . $foo;
}

print timeFunc('Method4', 10) . "\n";

Results were:

0.0014739
0.0015574
0.0011955
0.001169

当您只声明一个字符串而无需解析该字符串时,为什么要混淆 PHP 调试器来解析。我希望你明白我的意思。

【讨论】:

  • 你肯定无法通过现实生活中的考验。但是这个人造的科学怪人与现实生活条件毫无共同之处。
  • 那些试图在 PHP5+ 上重现这些结果的怀疑论者(比如我 ;-) 将 microtime() 调用更改为 microtime(true) - 您希望将时间作为浮点数,而不是某种奇怪的字符串。
  • 为字符串处理添加了 Method4()。而且我认为 PHP 处理现在变得更快了。 @AdamWright
  • 你好。您的比较假设字符串中只有一个变量实例。请看stackoverflow.com/a/31312867/2893496
  • 这让我很困惑:“大约 66% 的速度”,这不是“大约 66% 的时间”吗?我认为串联更快?
【解决方案3】:

实时基准测试:

http://phpbench.com/

用单引号和双引号连接变量时实际上存在细微差别。

【讨论】:

  • 我认为这取决于硬件配置和编译的php。
  • 我不知道你可以在 echo 中使用逗号,而不是点。
【解决方案4】:

使用@Adam 的测试

"these are " . $foo

请注意,以下内容甚至更快:

'these are ' . $foo;

这是因为一个双引号的“字符串”被评估,而单引号的“字符串”按原样...

【讨论】:

  • 我刚刚做了一些快速测试,这两者之间并没有多少节省 - 当然远不及将插值更改为串联 - 但单引号更快。
【解决方案5】:

不要太着迷于尝试在 PHP 中优化字符串操作。如果您的数据库查询写得不好或者您没有使用任何类型的缓存方案,则连接与插值是没有意义的(在现实世界的性能中)。以这样一种方式编写你的字符串操作,以便以后调试你的代码很容易,性能差异可以忽略不计。

@uberfuzzy 假设这只是一个关于语言细节的问题,我想这很好。我只是想补充一点,与实际性能下降(例如糟糕的数据库查询)相比,比较现实世界应用程序中单引号、双引号和 heredoc 之间的性能毫无意义。

【讨论】:

    【解决方案6】:

    执行时间的任何差异都可以忽略不计。

    请看

    不要在这样的微优化上浪费时间。使用分析器来衡量应用程序在真实场景中的性能,然后在真正需要的地方进行优化。与在整个代码中应用微优化相比,优化单个草率的 DB 查询可能会带来更大的性能提升。

    【讨论】:

    • 阿门!我注意到在不同的语言社区中,不同的东西被重视,单引号的性能似乎是 PHP 世界的神牛。
    【解决方案7】:

    连接变量时存在差异...以及您对结果所做的操作...以及您正在做的是将其转储到输出,是否正在输出缓冲。

    另外,服务器的内存情况如何?通常,高级平台上的内存管理比低级平台上的更差......

    $a = 'parse' . $this; 
    

    正在用户代码平台级别管理内存...

    $a = "parse $this";
    

    正在php系统代码平台级别管理内存...

    因此,这些与 CPU 相关的基准测试并不能说明全部情况。

    运行基准测试 1000 次与在尝试同时运行相同模拟 1000 次的服务器上运行基准测试 1000 次...根据应用程序的范围,您可能会得到截然不同的结果。

    【讨论】:

    • 加 1 表示“在较高级别的平台上的内存管理通常比在较低级别的平台上更糟糕...”
    【解决方案8】:

    我似乎记得论坛软件的开发者 Vanilla 用单引号替换了他代码中的所有双引号,并注意到性能有合理的提升。

    不过,我目前似乎无法找到讨论的链接。

    【讨论】:

      【解决方案9】:

      如果您在双引号字符串语法中使用变量,只需添加其他内容:

      $foo = "hello {$bar}";
      

      $foo = "hello $bar";
      

      而且这两个都比

      $foo = 'hello' . $bar; 
      

      【讨论】:

        【解决方案10】:

        双引号会慢得多。我从几个地方读到最好这样做

        'parse me '.$i.' times'
        

        "parse me $i times"
        

        虽然我会说第二个为您提供了更具可读性的代码。

        【讨论】:

        • 呃,不:根据我使用其他人代码的经验,第一个代码更具可读性。
        • @staticsan 让自己成为具有语法高亮功能的优秀编辑器,伙计。
        • 确实使用语法高亮编辑器。第一个变体的突出显示效果更好。
        • PhpStorm 编辑器可以很好地突出显示两个示例。
        • I read from several places - 什么地方?请提供文件。
        【解决方案11】:

        【讨论】:

          【解决方案12】:

          需要注意的是,当使用 Adam Wright 的示例的修改版本并带有 3 个变量时,结果是相反的,前两个函数实际上更快、更一致。这是 CLI 上的 PHP 7.1:

          function timeFunc($function, $runs)
          {
              $times = array();
          
              for ($i = 0; $i < $runs; $i++)
              {
                  $time = microtime();
                  call_user_func($function);
                  @$times[$i] = microtime() - $time;
              }
          
              return array_sum($times) / $runs;
          }
          
          function Method1()
          { 
              $foo = 'some words';
              $bar = 'other words';
              $bas = 3;
              for ($i = 0; $i < 10000; $i++)
                   $t = "these are $foo, $bar and $bas";
          }
          
          function Method2()
          {
              $foo = 'some words';
              $bar = 'other words';
              $bas = 3;
              for ($i = 0; $i < 10000; $i++)
                   $t = "these are {$foo}, {$bar} and {$bas}";
          }
          
          function Method3()
          {
              $foo = 'some words';
              $bar = 'other words';
              $bas = 3;
              for ($i = 0; $i < 10000; $i++)
                   $t = "these are " . $foo . ", " . $bar . " and " .$bas;
          }
          
          print timeFunc('Method1', 10) . "\n";
          print timeFunc('Method2', 10) . "\n";
          print timeFunc('Method3', 10) . "\n";
          

          我也尝试过使用 '3' 而不仅仅是整数 3,但我得到了相同的结果。

          $bas = 3:

          0.0016254
          0.0015719
          0.0019806
          

          使用 $bas = '3':

          0.0016495
          0.0015608
          0.0022755
          

          应该注意的是,这些结果差异很大(我得到的变化约为 300%),但平均值似乎相对稳定,几乎(10 个案例中有 9 个)总是显示前 2 种方法的执行速度更快,使用 Method 2 总是比方法 1 稍快。

          总之:对于 1 个单一操作(无论是插值还是串联)正确的情况对于组合操作并不总是正确的。

          【讨论】:

          • 我想知道这其中有多少来自翻转,有多少是 php7 中的优化。最初的问题是专门提到php5的上下文。
          【解决方案13】:

          是的,最初这是关于 PHP5,但几个月后 PHP8 出现了,今天在我的 PHP 7.4.5 上测试的最佳选择是使用 PHP - Nowdoc(在 WIN 10 + Apache 和 CentOs 上测试7 + 阿帕奇):

          function Method6(){
              $k1 = 'AAA';
              for($i = 0; $i < 10000; $i ++)$t = <<<'EOF'
          K1= 
          EOF
          .$k1.
          <<<'EOF'
          K2=
          EOF
          .$k1;
              }
          

          这里方法#5(使用Heredoc连接):

          function Method5(){
              $k1 = 'AAA';
              for($i = 0; $i < 10000; $i ++)$t = <<<EOF
          K1= $k1
          EOF
          .<<<EOF
          K2=$k1 
          EOF;
              }
          

          方法1到4在本文开头

          在我所有的测试中,“赢家”是方法#6(Newdoc),不是很容易阅读,但在 CPU 中速度非常快,并且曾经使用过 @Adam Wright 的函数 function timeFunc($function).

          【讨论】:

            【解决方案14】:

            我已经用以下测试用例测试了 php 7.4 和 php 5.4,但对我来说还是有点困惑。

            <?php
            $start_time = microtime(true);
            $result = "";
            for ($i = 0; $i < 700000; $i++) {
                $result .= "THE STRING APPENDED IS " . $i;
                // AND $result .= 'THE STRING APPENDED IS ' . $i;
                // AND $result .= "THE STRING APPENDED IS $i";
            }
            echo $result;
            $end_time = microtime(true);
            echo "<br><br>";
            echo ($end_time - $start_time) . " Seconds";
            

            PHP 7.4 输出

             1. "THE STRING APPENDED IS " . $i = 0.16744208335876
             2. 'THE STRING APPENDED IS ' . $i = 0.16724419593811
             3. "THE STRING APPENDED IS $i" = 0.16815495491028
            

            PHP 5.3 输出

             1. "THE STRING APPENDED IS " . $i = 0.27664494514465
             2. 'THE STRING APPENDED IS ' . $i = 0.27818703651428
             3. "THE STRING APPENDED IS $i" = 0.28839707374573
            

            我已经测试了很多次,在 php 7.4 中似乎所有 3 个测试用例都多次得到相同的结果,但串联在性能上仍然没有一点优势。

            【讨论】:

              【解决方案15】:

              根据@adam-wright 的回答,我想知道速度差异是否在没有连接/字符串中没有变量的情况下发生。

              == 我的问题...

              • $array['key'] 调用或设置比$array["key"] 快吗!?
              • $var = "some text";$var = 'some text'; 慢吗?

              == 我的测试 每次都使用新变量以避免使用相同的内存地址:

              function getArrDblQuote() { 
                  $start1 = microtime(true);
                  $array1 = array("key" => "value");
                  for ($i = 0; $i < 10000000; $i++)
                      $t1 = $array1["key"];
                  echo microtime(true) - $start1;
              }
              function getArrSplQuote() {
                  $start2 = microtime(true);
                  $array2 = array('key' => 'value');
                  for ($j = 0; $j < 10000000; $j++)
                      $t2 = $array2['key'];
                  echo microtime(true) - $start2;
              }
              
              function setArrDblQuote() { 
                  $start3 = microtime(true);
                  for ($k = 0; $k < 10000000; $k++)
                      $array3 = array("key" => "value");
                  echo microtime(true) - $start3;
              }
              function setArrSplQuote() {
                  $start4 = microtime(true);
                  for ($l = 0; $l < 10000000; $l++)
                      $array4 = array('key' => 'value');
                  echo microtime(true) - $start4;
              }
              
              function setStrDblQuote() { 
                  $start5 = microtime(true);
                  for ($m = 0; $m < 10000000; $m++)
                      $var1 = "value";
                  echo microtime(true) - $start5;
              }
              function setStrSplQuote() {
                  $start6 = microtime(true);
                  for ($n = 0; $n < 10000000; $n++)
                      $var2 = 'value';
                  echo microtime(true) - $start6;
              }
              
              print getArrDblQuote() . "\n<br>";
              print getArrSplQuote() . "\n<br>";
              print setArrDblQuote() . "\n<br>";
              print setArrSplQuote() . "\n<br>";
              print setStrDblQuote() . "\n<br>";
              print setStrSplQuote() . "\n<br>";
              

              == 我的结果:

              数组得到double引用2.1978828907013

              数组获取单个引用2.0163490772247

              数组集double引用1.9173440933228

              数组获取单个报价1.4982950687408

              var set double 引用 1.485809803009

              var set single 引用 1.3026781082153

              == 我的结论!

              因此,结果是差异不是很显着。但是,在一个大项目上,我认为它可以有所作为!

              【讨论】:

                猜你喜欢
                • 2013-05-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-03-09
                相关资源
                最近更新 更多