【问题标题】:PHP performance of using constants instead of strings使用常量而不是字符串的 PHP 性能
【发布时间】:2019-01-28 07:04:57
【问题描述】:

我正在用纯 PHP 创建一个 web 应用程序,我必须在函数之间交换一些状态,这个状态可以是 5 左右,例如:user_created、user_active、user_inactive 等等。那么,最有效的方法是什么?

最初我是从我的函数中返回字符串,但我知道字符串比较可能会很慢,而且如果状态名称变得越来越长,那么我会考虑创建具有整数值的常量,但它让我明白了思考...如果 PHP 解析器在比较之前仍然需要读取整个常量名称,这与使用字符串或变量不一样吗?有没有更有效的方法来做到这一点?

对不起,如果这看起来像一个微不足道的问题,我只是尝试优化我的应用程序的各个方面

如果不完全清楚我要做什么真的很简单:

function getUserStatus(){
    //return some value
}

然后按如下方式使用

$userStatus = getUserStatus
    switch ($userStatus){
    // all posible cases
}

【问题讨论】:

  • 如果您关心性能,您可以尝试分析这两种方法,看看哪一种性能更好。见Simplest way to profile a PHP script
  • 谢谢@Phil,如果有什么有趣的事情我会看看并回来发帖
  • 您在下面有一些很好的答案可以回答您的实际问题,所以我将介绍其他内容。 “我只是尝试优化我的应用程序的各个方面”。别。在你知道你必须解决之前不要解决这种事情。谷歌“过早优化”并熟悉该主题。在这种情况下,更好的考虑是代码的可维护性和清晰度,正如其他人所观察到的那样:在这种情况下使用常量更好。这就是你应该使用常量的原因。不在这里和那里节省奇数微秒。

标签: php php-7


【解决方案1】:

您可以使用以下内容对其进行测试。

请注意,PHP 进行了很多优化,并将编译后的字节码存储在其缓存中。 http://php.net/manual/en/intro.opcache.php

结果

字符串 0.38328790664673 持续的 0.50211310386658

字符串 0.38391804695129 持续的 0.51568698883057

令我惊讶的是,String 似乎更快。

我在 opcache 配置中记录了以下设置:

opcache.interned_strings_buffer integer
The amount of memory used to store interned strings, in megabytes. 
This configuration directive is ignored in PHP < 5.3.0.

一个非常简洁的设置,类似 0 文档。 PHP 使用一种称为字符串实习的技术来提高性能,例如,如果您的代码中有 1000 次字符串“foobar”,那么 PHP 会在内部为该字符串存储 1 个不可变变量,并为其他 999 使用指向它的指针您使用它的次数。凉爽的。此设置使其更上一层楼。而不是为每个 SINGLE php-fpm 进程拥有这些不可变字符串的池,此设置在所有 php-fpm 进程之间共享它。它可以节省内存并提高性能,尤其是在大型应用程序中。

所以说字符串比较比常量比较慢是 PHP 中的错误假设。

但是:您可以打破这个优化示例:

$state = "State";
switch($string) {
    case "Offline" . $state:
    break;
}

这样做的结果将是: 字符串 0.61401081085205 常量 0.51961803436279

在这种情况下,常量比较会更快。

添加到 PHP5.4 的性能改进,这里是 RFC https://wiki.php.net/rfc/performanceimprovements

但请注意,常量通常有助于更好地重构代码,从而更好地维护。此外,性能损失可以忽略不计

function doSomethingString() {
    return "OfflineState";
}

const OFFLINE_STATE = 1;
function doSomethingConstant() {
    return OFFLINE_STATE;
}

function dummy() {}

// String
echo('string' . PHP_EOL);
$start = microtime(true);
for($i = 0; $i < 10000000; $i++) {
    switch(doSomethingString()) {
        case "OfflineState":
            dummy();
            break;
    }
}
echo(PHP_EOL);
$end = microtime(true);
echo($end - $start);
echo(PHP_EOL);


//Constant
echo('constant' . PHP_EOL);
$start = microtime(true);
for($i = 0; $i < 10000000; $i++) {
    switch(doSomethingConstant()) {
        case OFFLINE_STATE:
            dummy();
            break;
    }
}
echo(PHP_EOL);
$end = microtime(true);
echo($end - $start);
echo(PHP_EOL);

我的php版本:

PHP 7.2.8-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: Jul 25 2018 10:52:19) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.8-1+ubuntu18.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies

【讨论】:

  • 谢谢你。我进行了类似的测试并得到了类似的结果。我对 PHP 中这方面的优化程度感到惊讶,我在 Python 等其他语言中得到了非常不同的结果。
  • 我正在浏览 opcache 文档,因为我真的很想了解原因。也许 Opcache 使用了不可变的字符串或其他东西。
  • 我将尝试在 PHP 5.4 上运行相同的测试,看看是否仍然存在这种差异。
  • 找到了! opcache.interned_strings_buffer 似乎在这里起作用,字符串被缓存
  • 该设置创建了一个带有不可变字符串的缓冲区,因此字符串比较比基于整数的常量比较快
【解决方案2】:

我知道字符串比较可能很慢

这在网络应用的上下文中并不准确。

字符串比较需要 微秒

加载网页、访问数据库等需要 毫秒(长数千倍)。

所以这种微优化几乎不值得。相反,您应该专注于使代码最清晰、最易于维护的因素。

如果您要循环处理数百万个项目或每秒处理数千个请求,则代码的性能优化稍后会发挥作用。

如果PHP解析器在比较之前还需要读取整个常量名,那不就和使用字符串或者变量一样吗?

使用常量(并且常量的值是数字而不是字符串)可能比使用字符串值稍微快一些,而且代码也可能更清晰和可维护。但同样,在加载网页的过程中,您将无法区分。

【讨论】:

  • “使用常量(并且常量的值是数字而不是字符串)可能比使用字符串值稍微快一些”显然并不令我惊讶
  • 但是我明白了你的意思,我刚刚读了一个让我思考的引文,它说“计算机电源便宜,内存便宜,开发人员时间很贵”,基本上是告诉我,如果我是要做出选择,我必须更喜欢可修改性而不是原始性能。
  • @Dknacht 常量提供更好的可维护性,因为重构代码变得更容易,是的,这更重要
【解决方案3】:

我将解决您问题的几个方面,希望能给您更广泛的理解。

对于您的实际问题,答案是应用程序的运行时阶段和解析阶段是不同的概念。 PHP 确实必须解析您的源代码,但该源代码随后会转换为另一种称为 Opcode 的格式。操作码是代码的较低级别、更有效的表示。运行时还会缓存此表示,这意味着它不必在每次调用时解析您的源代码。

比较两个字符串时,所花费的时间取决于最短字符串的长度(一般来说)。但是,整数通常可以更有效地进行比较,这就是您尝试通过使用基于整数的常量来实现的性能优势。常量值的查找不一定需要将常量的名称作为字符串遍历,因为它在操作码中的表示不一定与源代码中的相同。但是,正如另一个答案中所指出的,PHP 似乎实际上针对字符串比较进行了优化,并且它们实际上在很小程度上优于常量整数比较!

但是,这并不一定意味着您不应该切换到常量。在这些类型的比较中使用常量与字符串文字的真正好处是,运行时能够在您打错字时帮助您。如果您的代码库中充斥着$var == "some special value" 类型的字符串比较,解析器很乐意让您在代码的晦涩区域中引入"some special value" 中的错字。但是,如果您使用常量,如果您输入错误的常量名称,$var == MY_SPECIAL_CONSTANT 之类的比较会警告您。它还允许您在单个位置定义具体值(常量定义),这样如果您需要更改它,只需一行更改即可,而不是大量的查找替换工作。

很多 PHP 代码往往是“基于字符串的编程”,这些类型的代码库可能很脆弱并且容易出现拼写错误。常量在这方面真的很有帮助,而这正是它们的价值所在,而不是性能。

【讨论】:

  • 字符串比较似乎比基于整数的常量更快(令我惊讶的是),所以说“这就是你试图通过使用基于整数的常量来实现的性能优势”似乎是无效的
  • 这确实令人惊讶。然而,这是可以在 PHP 版本之间轻松更改的类型 - 我们正在谈论微秒。这无疑是他试图实现的感知收益,即使目前这是一种看似有缺陷的方法。
  • 在我自己的答案中添加了解释,opcache 对此有一个设置opcache.interned_strings_buffer
  • 很棒的发现!我也编辑了我的答案以反映您的发现。我认为通常用于基于字符串的编程的语言会针对字符串比较进行大量优化是有道理的。我很惊喜他们能做得这么好。要么,要么他们在常量整数比较方面有很多工作要做!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-02
  • 1970-01-01
  • 1970-01-01
  • 2011-05-13
  • 1970-01-01
  • 2016-05-30
  • 2016-10-07
相关资源
最近更新 更多