28

关于性能,执行以下操作有什么区别:

$message = "The request $request has $n errors";

$message = sprintf('The request %s has %d errors', $request, $n);

在 PHP 中?

我会说调用函数涉及更多的东西,但我不知道 PHP 在幕后做了什么来扩展变量名称。

谢谢!

4

6 回答 6

40

不要紧。

任何性能提升都将是如此微不足道,以至于您只会在 10000 或 100000 次迭代中看到它(作为百分之几秒的改进)——即使如此。

有关具体数字,请参阅此基准。您可以看到它必须使用 100,000 次函数调用生成 1MB+ 的数据才能在数百毫秒内实现可测量的差异。几乎不是现实生活中的情况。即使是最慢的方法(“sprintf() with positional params”)也只需要 0.00456 毫秒,而最快的方法是 0.00282 毫秒。对于任何需要 100,000 次字符串输出调用的操作,您将有其他因素(例如网络流量)比通过优化可以节省的 100 毫秒慢一个数量级。

使用任何使您的代码对您和其他人来说最易读和可维护的东西。对我个人而言,该sprintf()方法是一个巧妙的想法——我必须考虑自己开始使用它。

于 2011-08-22T12:15:44.787 回答
31

在所有情况下,第二个都不会更快,因为您提供了一个双引号字符串,它也必须解析为变量。如果要进行微优化,正确的方法是:

$message = sprintf('The request %s has %d errors', $request, $n);

尽管如此,我相信秒数会变慢(正如@Pekka 指出的差异实际上并不重要),因为函数调用、解析字符串、转换值等的开销。但是请注意,2 行代码不是等效,因为在第二种情况下 $n 被转换为整数。如果 $n 是“无错误”,那么第一行将输出:

The request $request has no error errors

而第二个将输出:

The request $request has 0 errors
于 2011-08-22T12:25:20.350 回答
21

此处进行了关于“变量扩展与 sprintf”的性能分析。

正如@pekka 所说,“使您的代码对您和其他人来说最具可读性和可维护性”。当性能增益“低”(~小于两倍)时,忽略它。

总结基准:PHP 针对双引号和 Heredoc 分辨率进行了优化。尊重平均时间的百分比,仅使用计算很长的字符串,

  • 双引号分辨率:75%
  • heredoc 分辨率:82%
  • 单引号连接:93%
  • sprintf 成型:117%
  • 带有索引参数的 sprintf 格式化:133%

请注意,只有 sprintf 执行一些格式化任务(请参阅基准的 '%s%s%d%s%f%s'),并且正如@Darhazer 所示,它对输出有一些影响。更好的测试是两个基准测试,一个只比较连接时间('%s' 格式化程序),其他包括格式化过程 - 例如 '%3d%2.2f' 和在将变量扩展为双引号之前的等效功能......还有更多使用短模板字符串的基准组合。

优点和缺点

正如基准所显示的那样,它的主要优点sprintf是非常低成本的格式化程序(!)。对于通用模板,我建议使用vsprintf函数。

双引号(和heredoc)的主要优点是一些性能;与 sprintf 的位置标记相比,标称占位符的一些可读性和可维护性随着参数的数量(在 1 之后)而增长。

索引占位符的使用是 sprintf 可维护性的一半。

注意:不要使用单引号连接,只有在确实需要时。请记住,PHP 启用了安全语法,例如"Hello {$user}_my_brother!"和引用,例如"Hello {$this->name}!".

于 2012-08-31T13:42:17.313 回答
4

我很惊讶,但对于 PHP 7.*,“$variables replacement”是最快的方法

$message = "The request {$request} has {$n} errors";

您可以简单地自己证明:

$request = "XYZ";
$n = "0";
$mtime = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
        $message = "The request {$request} has {$n} errors";
}
$ctime = microtime(true);
echo '

"variable $replacement timing": '.  ($ctime-$mtime);




$request = "XYZ";
$n = "0";
$mtime = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
        $message = 'The request '.$request.' has '.$n.' errors';
}
$ctime = microtime(true);
echo '

"concatenation" . $timing: '.  ($ctime-$mtime);



$request = "XYZ";
$n = "0";
$mtime = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
        $message = sprintf('The request %s has %d errors', $request, $n);
}
$ctime = microtime(true);
echo '

sprintf("%s", $timing): '.  ($ctime-$mtime);

PHP 7.3.5的结果:

“可变$更换时间”:0.091434955596924

“串联”。$计时:0.11175799369812

sprintf("%s", $timing): 0.17482495307922

可能您已经找到了诸如“使用 sprintf 而不是双引号中包含的变量,它的速度大约快 10 倍”之类的建议。有哪些好的 PHP 性能提示?

我知道这是事实,但总有一天。即在PHP 5.2.*之前

以下是当时PHP 5.1.6的示例:

“可变$更换时间”:0.67681694030762

“串联”。$计时:0.24738907814026

sprintf("%s", $timing): 0.61580610275269

于 2019-11-18T14:46:33.810 回答
2

对于将多个字符串变量注入字符串,第一个会更快。

$message = "The request $request has $n errors";

并且对于单次注入,点(。)连接会更快。

$message = 'The request '.$request.' has 0 errors';

用十亿循环进行迭代并找出差异。

例如:

<?php

    $request = "XYZ";
    $n = "0";
    $mtime = microtime(true);
    for ($i = 0; $i < 1000000; $i++) {
            $message = "The request {$request} has {$n} errors";
    }
    $ctime = microtime(true);
    echo ($ctime-$mtime);

?>
于 2014-02-10T10:53:56.437 回答
0

最终,在考虑单个变量赋值的上下文时,第一个是最快的,这可以通过查看各种基准来看到。不过,也许使用核心 PHP 函数的 sprintf 风格可以允许更多可扩展的代码,并针对 opcache 或 apc 等字节码级缓存机制进行更好的优化。换句话说,特定大小的应用程序在使用 sprintf 方法时可以使用更少的代码。您需要缓存到 RAM 中的代码越少,用于其他事情或更多脚本的 RAM 就越多。但是,这仅在您的脚本无法使用评估正确放入 RAM 时才重要。

于 2016-06-15T00:03:06.980 回答