为什么strtolower比strtoupper略慢?

时间:2017-06-27 13:51:48

标签: php c performance micro-optimization

我出于好奇做了一个实验。我想看看strtolower()strtoupper()之间是否存在微观差异。我预计strtolower()在大多数小写字符串上会更快,反之亦然。我发现strtolower()在所有情况下都较慢(尽管在你做了数百万次之前完全无关紧要。)这是我的考验。

$string = 'hello world';
$start_time = microtime();
for ($i = 0; $i < 10000000; $i++) {
    strtolower($string);
}
$timed = microtime() - $start_time;
echo 'strtolower ' . $string . ' - ' . $timed . '<br>';

strtolower()strtoupper()hello worldHELLO WORLDHello World重复。 Here is the full gist.我已多次运行代码并继续获得大致相同的结果。这是以下一次测试。

strtolower hello world - 0.043829
strtoupper hello world - 0.04062
strtolower HELLO WORLD - 0.042691
strtoupper HELLO WORLD - 0.015475
strtolower Hello World - 0.033626
strtoupper Hello World - 0.017022

我相信php-src github中控制此问题的C代码适用于strtolower(),此处代表strtoupper()

要明确,这不会阻止我使用strtolower()。我只是想了解这里发生了什么。

为什么strtolower()strtoupper()慢?

2 个答案:

答案 0 :(得分:4)

它主要取决于您当前使用的字符编码,但速度差异的主要原因是特殊字符的每个编码字符的大小。

取自babelstone.co.uk

  

例如,带有caron(ǰ)的小写j表示为单个编码字符(U + 01F0 LATIN SMALL LETTER J WITH CARON),但相应的大写字符(J̌)在Unicode中表示为序列两个编码字符(U + 004A LATIN CAPITAL LETTER J + U + 030C COMBINING CARON)。

在Unicode字符索引中筛选更多数据将不可避免地需要更长时间。

请注意,strtolower使用您当前的区域设置,因此如果您的服务器使用的字符编码不支持strtolower特殊字符(例如'Ê'),那么只会返回特殊字符。但是,设置了UTF-8上的字符映射,可以通过运行mb_strtolower来确认。

还有可能将属于uppercase类别的字符数与您在lowercase类别中找到的数量进行比较,但这又取决于您的角色编码

简而言之,strtolower有一个更大的字符数据库,用于比较每个字符串字符与检查字符是否为uppercase时的字符。

答案 1 :(得分:4)

代码的实现存在一些非常小的差异:

PHPAPI char *php_strtoupper(char *s, size_t len)
{
    unsigned char *c, *e;

    c = (unsigned char *)s;
    e = (unsigned char *)c+len;    <-- strtolower uses e = c+len;

    while (c < e) {
        *c = toupper(*c);
        c++;
    }
    return s;
}

PHPAPI zend_string *php_string_toupper(zend_string *s)
{
    unsigned char *c, *e;

    c = (unsigned char *)ZSTR_VAL(s);
    e = c + ZSTR_LEN(s);

    while (c < e) {
        if (islower(*c)) {
            register unsigned char *r;
            zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);

            if (c != (unsigned char*)ZSTR_VAL(s)) {
                memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
            }
            r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
            while (c < e) {
                *r = toupper(*c);
                r++;
                c++;
            }
            *r = '\0';
            return res;
        }
        c++;
    }
    return zend_string_copy(s);
}

PHP_FUNCTION(strtoupper)
{
    zend_string *arg;      <-- strtolower uses zend_string *str;

    ZEND_PARSE_PARAMETERS_START(1, 1)
        Z_PARAM_STR(arg)          <-- strtolower uses Z_PARAM_STR(str)
    ZEND_PARSE_PARAMETERS_END();

    RETURN_STR(php_string_toupper(arg));     <-- strtolower uses RETURN_STR(php_string_tolower(str));
}

和strtolower

PHPAPI char *php_strtolower(char *s, size_t len)
{
    unsigned char *c, *e;

    c = (unsigned char *)s;
    e = c+len;                  <-- strtoupper uses e = (unsigned char *)c+len;

    while (c < e) {
        *c = tolower(*c);
        c++;
    }
    return s;
}

PHPAPI zend_string *php_string_tolower(zend_string *s)
{
    unsigned char *c, *e;

    c = (unsigned char *)ZSTR_VAL(s);
    e = c + ZSTR_LEN(s);

    while (c < e) {
        if (isupper(*c)) {
            register unsigned char *r;
            zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);

            if (c != (unsigned char*)ZSTR_VAL(s)) {
                memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
            }
            r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
            while (c < e) {
                *r = tolower(*c);
                r++;
                c++;
            }
            *r = '\0';
            return res;
        }
        c++;
    }
    return zend_string_copy(s);
}

PHP_FUNCTION(strtolower)
{
    zend_string *str;     <-- strtoupper uses zend_string *arg; 

    ZEND_PARSE_PARAMETERS_START(1, 1)
        Z_PARAM_STR(str)        <-- strtoupper uses Z_PARAM_STR(arg)
    ZEND_PARSE_PARAMETERS_END();

    RETURN_STR(php_string_tolower(str));    <-- strtoupper uses RETURN_STR(php_string_tolower(arg));
}

这些微小的差异是否足以影响这几纳秒的性能,我不知道......不确定为什么差异就在那里