【问题标题】:PHP - Why is comparison of 2 entire long (identical) strings MUCH faster than comparison of the first character of each?PHP - 为什么比较两个完整的长(相同)字符串比比较每个字符串的第一个字符快得多?
【发布时间】:2016-05-16 20:44:15
【问题描述】:

对于我的一个应用程序,我假设比较 2 个字符串的第一个字符会比比较整个字符串的相等性更快。例如,如果我知道只有 2 个可能的字符串(在一组 n 字符串中)可以以相同的字母开头(比如说一个 'q'),如果是这样,它们是相同的字符串,那么我可能会写一个这样的比较:

if ($stringOne[0] === $stringTwo[0]) $qString = true;

代替:

if ($stringOne === $stringTwo) $qString = true;

但我最近写了一些基准脚本,似乎我假设错了。也就是说,看起来第二次比较平均比第二次快 2-4 倍。我的基准是这样的:

$x = 'A really really looooooooooooong string';
$y = 'A really really looooooooooooong string';

$timeArray = array();

//Method 1, two-four times faster than Method 2
for($i = 0; $i < 100; $i++) {
    $t1 = microtime(true);
    for($j = 0; $j < 100000; $j++) {
        if ($x === $y) continue;
    }
    $t2 = microtime(true);
    $timeArray[] = $t2 - $t1;
}

echo array_sum($timeArray) / 100;//average time is echoed

//Method 2
for($i = 0; $i < 100; $i++) {
    $t1 = microtime(true);
    for($j = 0; $j < 100000; $j++) {
        if ($x[0] === $y[0]) continue;
    }
    $t2 = microtime(true);
    $timeArray[] = $t2 - $t1;
}

echo array_sum($timeArray) / 100;//average time is echoed

我想我假设由于每个字符串 $x 和 $y 都在内存中,那么每个字符串的第一个字符也在内存中,并且比较会更快。

为什么全字符串比较更快?从每个字符串中提取第一个字符进行比较是否有“成本”?

更新:即使在每次外部循环迭代中生成新字符串并进行比较,或者起始字符串是否相同,Method1 对我来说仍然比 Method2 快。

//Method 1 faster than Method 2 by 2-3 times
for($i = 0; $i < 100; $i++) {
    $t1 = microtime(true);
    $a = $x . $i;
    $b = $y . $i;
    for($j = 0; $j < 100000; $j++) {
        if ($a === $b) continue;
    }
    $t2 = microtime(true);
    $timeArray[] = $t2 - $t1;
}

//Method 2
for($i = 0; $i < 100; $i++) {
    $t1 = microtime(true);
    $a = $x . $i;
    $b = $y . $i;
    for($j = 0; $j < 100000; $j++) {
        if ($a[0] === $b[0]) continue;
    }
    $t2 = microtime(true);
    $timeArray[] = $t2 - $t1;
}

如果通过严格不等价而不是严格等价比较两者,也会得到相同的结果

//Method 1 faster than Method 2 by 1.5-2 times, but now less of a difference
for($i = 0; $i < 100; $i++) {
    $t1 = microtime(true);
    $a = $x . $i;
    $b = $y . $i;
    for($j = 0; $j < 100000; $j++) {
        if ($a !== $b) continue; // using inequivalence this time
    }
    $t2 = microtime(true);
    $timeArray[] = $t2 - $t1;
}

//Method 2
for($i = 0; $i < 100; $i++) {
    $t1 = microtime(true);
    $a = $x . $i;
    $b = $y . $i;
    for($j = 0; $j < 100000; $j++) {
        if ($a[0] !== $b[0]) continue;  // using inequivalence this time
    }
    $t2 = microtime(true);
    $timeArray[] = $t2 - $t1;
} 

【问题讨论】:

  • 什么版本的 PHP?字符串是硬编码的、从文件中读取的还是动态生成的?
  • 很可能是由于 zval 引用效应
  • 对于这个用例只有硬编码...在我的应用程序字符串是动态生成的

标签: php performance


【解决方案1】:

静态字符串,如脚本中的will be interned(有关其含义的详细说明,请参见维基百科上的String Interning)。

本质上,这意味着相同的字符串只会在内存中存储一​​次。当 PHP 进行比较时,它会立即看到两个字符串都引用了内存中的同一个对象,并且不需要做任何进一步的检查。字符串中单个字符的比较很可能不会从这种优化中受益,这就是它们可能需要更长时间的原因。

很可能还有其他因素在起作用,但这将是一个主要因素。尝试动态构造一个或两个字符串,并通过如下更改代码来查看结果有多大变化:

$x = base64_encode(base64_decode('A really really looooooooooooong string'));

正如 cmets 中所承诺的,这是一个脚本版本,它覆盖了字符串实习和任何可能正在使用的相等缓存。

我在这里得到的结果表明第二种方法要快得多。

<?php
$runs = 1000000;
$input_string_a = "A really really looooooooooooong string";
$input_string_b = "B really really looooooooooooong string";

$total_time = 0;
for($i=0; $i<$runs; $i++) {
    $a = substr($input_string_a, 0);
    $b = substr($input_string_b, 0);
    $start = microtime(true);
    if($a === $b) {
        if(false) break;
    }
    $end = microtime(true);
    $total_time += $end - $start;
}

echo $total_time."\n";

$total_time = 0;
for($i=0; $i<$runs; $i++) {
    $a = substr($input_string_a, 0);
    $b = substr($input_string_b, 0);
    $start = microtime(true);
    if($a[0] === $b[0]) {
        if(false) break;
    }
    $end = microtime(true);
    $total_time += $end - $start;
}

echo $total_time."\n";

【讨论】:

  • 我将 $x 更新为 md5('original string') 并将 $y 更新为 'original string' 。 time() 和结果基本相同....我知道的非等效字符串但结果仍然相同
  • 这是我得到的结果。对于您的原始代码(编译时匹配的字符串)0.008, 0.026。我的修改(字符串匹配,未在编译时确定)0.021 0.030。如果字符串在编译时和运行时都不匹配(在第一个字符上),我也得到了相同的结果。
  • 这向我表明字符串实习是一个重要因素,但可能还有其他关于字符串比较的优化可以使其更快。
  • 当字符串不等价时,您是否也比 Method2 更快地获得 Method1?由于我的应用程序具有可以运行数千次的循环,因此 2-3 倍的时间差很重要。我可能需要重写一些代码
  • 第一个字符比较比较快,但不是很多。
猜你喜欢
  • 2011-06-21
  • 2012-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-12
  • 2015-05-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多