PHP列表+分裂VS子字符串+查找位置-哪个更有效?

9

示例文本:

$text = 'Administration\Controller\UserController::Save';

任务-提取“::”之前的所有内容。
选项1:
list($module) = explode('::',$text);

方案二:

$module = substr($text, 0, strpos($text, '::');

哪个选项更有效率?

1
测试一下,比在这里询问要快得多。 - N.B.
1
你认为应该让explode返回数组,而substr返回字符串的一部分吗? - Shushant
测试一下并分享结果 ;) - Elon Than
没关系,两种方法都足够快。我更喜欢使用 explode,但大多数人会说 substr 更有效。 - Flash Thunder
如果处理的数据量较小,则性能因素可以忽略不计。 - Shushant
显示剩余2条评论
6个回答

15

我运行了一个测试,似乎第一个解决方案更快。这是用于测试的代码:

function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

function solution1($text)
{
    for($i = 0; $i < 10000; $i++)
        list($module) = explode('::',$text);
}

function solution2($text)
{
    for($i = 0; $i < 10000; $i++)
        $module = substr($text, 0, strpos($text, '::'));
}

$text = 'Administration\Controller\UserController::Save';

$time_start = microtime_float();

solution1($text);

$time_end = microtime_float();
$time = $time_end - $time_start;

echo "Did solution1 in $time seconds.\n";

$time_start = microtime_float();

solution2($text);

$time_end = microtime_float();
$time = $time_end - $time_start;

echo "Did solution2 in $time seconds.\n";

测试 1: 在0.19701099395752秒内完成解决方案1。 在0.38502216339111秒内完成解决方案2。

测试 2: 在0.1990110874176秒内完成解决方案1。 在0.37402105331421秒内完成解决方案2。

测试 3: 在0.19801092147827秒内完成解决方案1。 在0.37002205848694秒内完成解决方案2。


1
你应该提供结果 :) - andy
1
测试1: 解决方案1在0.19701099395752秒内完成。 解决方案2在0.38502216339111秒内完成。测试2: 解决方案1在0.1990110874176秒内完成。 解决方案2在0.37402105331421秒内完成。测试3: 解决方案1在0.19801092147827秒内完成。 解决方案2在0.37002205848694秒内完成。 够了吗? :P - lu cip
太棒了!结果非常有趣。 - andy
我的结果有些复杂:手动运行脚本多次,我得到了不同的结果 - 但大多数情况下,选项1比较慢,但差距很小... 我想结论是 - 在非性能关键系统上使用哪个选项都无所谓。 - Katya S
1
PHP 7.2 在 Ubuntu 上:解决方案1用时0.01142秒。解决方案2用时0.00609秒。 - jchook
显示剩余5条评论

9

substr+strpos将更快,占用较少的CPU时间和内存。

让我们从php源代码中找出答案。

首先使用explode

PHP_FUNCTION(explode) 
{ 
    // other codes

    array_init(return_value); 

    if (str_len == 0) { 
        if (limit >= 0) { 
            add_next_index_stringl(return_value, "", sizeof("") - 1, 1); 
        } 
        return; 
    } 


    // other code
    if (limit > 1) { 
        php_explode(&zdelim, &zstr, return_value, limit); 
    } else if (limit < 0) { 
        php_explode_negative_limit(&zdelim, &zstr, return_value, limit); 
    } else { 
        add_index_stringl(return_value, 0, str, str_len, 1); 
    } 
}


PHPAPI void php_explode(zval *delim, zval *str, zval *return_value, long limit)
{
    char *p1, *p2, *endp;

    endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);

    p1 = Z_STRVAL_P(str);
    p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);

    if (p2 == NULL) {
        add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1);
    } else {
        do { 
            add_next_index_stringl(return_value, p1, p2 - p1, 1);
            p1 = p2 + Z_STRLEN_P(delim);
        } while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL &&
                 --limit > 1);

        if (p1 <= endp)
            add_next_index_stringl(return_value, p1, endp-p1, 1);
    }    
}

explode函数会多次调用php_memnstradd_next_index_stringl函数,这些函数会对结果列表进行操作。

现在看看strpos函数:

PHP_FUNCTION(strpos)
{
    zval *needle;
    char *haystack;
    char *found = NULL;
    char  needle_char[2];
    long  offset = 0;
    int   haystack_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &haystack, &haystack_len, &needle, &offset) == FAILURE) {
        return;
    }

    if (offset < 0 || offset > haystack_len) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string");
        RETURN_FALSE;
    }

    if (Z_TYPE_P(needle) == IS_STRING) {
        if (!Z_STRLEN_P(needle)) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty needle");
            RETURN_FALSE;
        }

        found = php_memnstr(haystack + offset,
                            Z_STRVAL_P(needle),
                            Z_STRLEN_P(needle),
                            haystack + haystack_len);
    } else {
        if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
            RETURN_FALSE;
        }
        needle_char[1] = 0;

        found = php_memnstr(haystack + offset,
                            needle_char,
                            1,
                            haystack + haystack_len);
    }

    if (found) {
        RETURN_LONG(found - haystack);
    } else {
        RETURN_FALSE;
    }
}

PHP_FUNCTION(substr)
{
    // other code about postion
    RETURN_STRINGL(str + f, l, 1);
}

它只调用了php_memnstr一次,而substr在内存中操作输入字符串,返回子字符串。


对我来说很有效...如果你有一个像'a#b'这样的字符串,想要分割它,substr+strpos更快。至少对我来说是这样的。对于1000000次操作,explode慢了0.02秒!"explode"函数就是为此而生的,所以我认为我会使用它。代码更清晰。没有时间重新发明轮子。 - StefanNch

7

在我的系统上:

~/pb$ uname -a && php -v
Linux hostname 3.2.0-4-amd64 #1 SMP Debian 3.2.46-1+deb7u1 x86_64 GNU/Linux
PHP 5.4.19-1~dotdeb.1 (cli) (built: Aug 27 2013 00:42:43) 
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies
    with XCache v3.0.3, Copyright (c) 2005-2013, by mOo
    with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans
    with XCache Cacher v3.0.3, Copyright (c) 2005-2013, by mOo

我有结果:
~/pb$ ./test ListVsSubstr
[============================================================>] 1000 u | 8134 u/s | Est: 0.0 s | Mem: 335.74 KB | Max: 357.96 KB
[============================================================>] 1000 u | 7808 u/s | Est: 0.0 s | Mem: 336.14 KB | Max: 357.96 KB
Test name       Repeats         Result          Performance 
list+explode    1000            0.044890 sec    +0.00%
substr+strpos   1000            0.052825 sec    -17.68%

这里有个测试代码:链接

不同的系统和PHP版本可能会产生不同的结果。您需要自己检查,确保在与您的生产环境相同的环境配置下进行测试。但总体来说,list+explode始终比其他方法快15%以上。


1
很好的测试集!感谢您的时间! - Katya S

1

测试:带有限制的explode(解决方案3) 测试:preg_match

注意: 10000对我来说不够,所以我使用了10 000 000 x 3。

<?php
function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

function solution1($text)
{
    for($i = 0; $i < 10000000; $i++)
        list($module) = explode('::',$text);
}

function solution2($text)
{
    for($i = 0; $i < 10000000; $i++)
        $module = substr($text, 0, strpos($text, '::'));
}

function solution3($text)
{
    for($i = 0; $i < 10000000; $i++)
        list($module) = explode('::',$text, 2);
}

function solution4($text)
{
    for($i = 0; $i < 10000000; $i++)
        preg_match('/^(.*)::/', $text, $m) && $module = $m[1];

}

$text = 'Administration\Controller\UserController::Save';

for ($i=0; $i < 3; $i++) {
    $time_start = microtime_float();

    solution1($text);

    $time_end = microtime_float();
    $time = $time_end - $time_start;

    echo "Did solution1 in $time seconds.\n";

    $time_start = microtime_float();

    solution2($text);

    $time_end = microtime_float();
    $time = $time_end - $time_start;

    echo "Did solution2 in $time seconds.\n";

    $time_start = microtime_float();

    solution3($text);

    $time_end = microtime_float();
    $time = $time_end - $time_start;

    echo "Did solution3 in $time seconds.\n";
}

而结果是:

Did solution1 in 6.4486601352692 seconds.
Did solution2 in 9.4331159591675 seconds.
Did solution3 in 6.6791591644287 seconds.
Did solution4 in 9.3652379512787 seconds.

Did solution1 in 7.1072399616241 seconds.
Did solution2 in 10.755952835083 seconds.
Did solution3 in 7.5958750247955 seconds.
Did solution4 in 9.4377269744873 seconds.

Did solution1 in 7.4207429885864 seconds.
Did solution2 in 10.894104003906 seconds.
Did solution3 in 7.701789855957 seconds.
Did solution4 in 9.5081558227539 seconds.

解决方案3比解决方案1需要更长的时间!在CLI上运行,1个线程占用100%的CPU。

我在本地运行了这些测试,当定界符在干草堆中出现多次时(即使用分隔符“'”将$text ='The quick brown fox jumped over the lazy dog'; 分割),则solution3solution1快得多。 - Brandon
考虑到目标字符串(上述文本)中包含更多的分隔符时,解决方案2将显著更快。正如其他答案中提到的那样,它还将使用更少的内存。 在2.9455399513245秒内完成了解决方案1。 在0.7404088973999秒内完成了解决方案2。 在1.1065330505371秒内完成了解决方案3。 PHP 7.4 - Jack B

0

在Linux上使用time命令。第一个测量时间为0m0.024s,第二个为0m0.011s

第二个似乎更快。我运行了多次,结果(除了一次)似乎都是相同的。

编辑:如建议所述,另一个用户建议将其循环5000次。结果与之前相同。


0.024秒和0.011秒? 单个命令? 我在0.012秒内得到了1000。 - Flash Thunder
我不确定你的意思。你能解释一下吗? - andy
那些时间是什么?24毫秒花费了什么时间? - Flash Thunder
我尽可能地让它清晰明了。我使用了 time php file.php,第一个是关于 list/explode 的,第二个是 strpos - andy
我认为这更多是因为PHP需要启动的时间,而每个请求所需的时间非常微不足道,所以并没有什么区别。 - andy

-2

高效?如果你指的是执行时间,那么就将每个程序在循环中运行多次(1000次),然后检查执行时间。


1
我发布这个问题是为了分享知识。如果您想要帮助,请提供您的机器规格和测试结果。 - Katya S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接