为什么 strtolower 比 strtoupper 稍慢?

Why is strtolower slightly slower than strtoupper?

出于好奇,我做了一个实验。我想看看 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>';

hello worldHELLO WORLDHello World 重复 strtolower()strtoupper()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() and here for strtoupper()

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

为什么 strtolower()strtoupper() 慢?

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

摘自babelstone.co.uk:

For example, lowercase j with caron (ǰ) is represented as a single encoded character (U+01F0 LATIN SMALL LETTER J WITH CARON), but the corresponding uppercase character (J̌) is represented in Unicode as a sequence of two encoded characters (U+004A LATIN CAPITAL LETTER J + U+030C COMBINING CARON).

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

请记住,strtolower 使用您当前的语言环境,因此如果您的服务器使用的字符编码不支持 strtolower 特殊字符(例如 'Ê '), 它只是 return 特殊字符。 UTF-8 上的字符映射是设置的,可以通过 运行 mb_strtolower.

确认

也可以将属于 uppercase 类别的字符数与 lowercase 类别中的字符数进行比较,但这又取决于你的字符编码。

简而言之,strtolower 有一个更大的字符数据库,可以在检查字符是否为 uppercase.

时将每个单独的字符串字符进行比较

代码的实现有几个非常细微的差异:

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 = '[=10=]';
            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 = '[=11=]';
            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));
}

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