字符串使缓慢的表演变得生动起来


String camelize slow performance

所以我在一个名为Camelize 的静态方法中有这段代码

return strtr(ucwords(strtr($id, array('_' => ' ', '.' => '_ ', '''' => '_ '))), array(' ' => ''));

这只是客串的东西。

我有大约211k条记录,经过一段时间的循环迭代,这些记录中有一些单词我需要camalize,最长的单词可以是大约10个字符,很容易,但通过运行一些xhprof测试,我得出结论,如果你需要速度,camalize不是一个好主意。

XHProf带camalize:313866303微秒(~5分钟)

  • 19268795个呼叫
  • 其中。Wall Time 228658500
  • ICpu:81.3%

XHProf无camalize:55099811微秒(<1min)

此脚本的目的是为类属性设置值。

一个属性可以是protected $myVar,它是camalized的。

在我的构造函数中,我得到了下划线属性(数组键)及其值(数组值)的数组。my_var => foo

由于我们有camalize属性,我们需要将数组键转换为camalize,这样我们就可以执行以下操作$this->$camelizedProperty = $value

是的,我们本来可以使用下划线属性,这样我们就不需要转换任何字符串,但这是一个旧的工作代码,现在它的属性大约是原来的4倍,而且它正被许多依赖项使用,所以如果我们能找到一种加快camalizing的方法,那么现在就不能将属性更改为下划线。

更新

使用microtime的一些单独测试来进行实时比较,我最终得到了这个。。。

使用的方法:

  1. strtr(ucwords(strtr($word, array('_' => ' ', '.' => '_ ', '''' => '_ '))), array(' ' => ''))
  2. lcfirst(str_replace(" ", "", ucwords(strtr($word, "_-", " "))))
  3. str_replace(" ", "", ucwords(strtr($word, "_-", " ")))

每次迭代约100个字符串的平均结果:

  1. 0.0011
  2. 0.0002
  3. 0.0002

因此,正如@RST所指出的,使用str_replace比我的第一种方法快18%,但仍然很慢(如果我们有一个巨大的循环)

在20M记录下,使用xhprof,总结果为:

  1. 313秒(~5分钟)
  2. 152秒(~2.5分钟)
  3. 158秒(~2.5分钟)

我们可以说lcfirst不会减慢脚本的速度(我认为这可能会导致一些时间变慢)。

这个问题不是关于如何camalize,而是关于它如何影响我们脚本中的性能,以及使用它的最佳方式

好吧,根据获得更快的时间的重要性,您可以用每个输入字符串的单程字符串迭代来替换内置的PHP函数调用。

2000万条记录的时间如下:

Method 1 time: 301.143823 
Method 2 time: 54.648126

诚然,这里的代码看起来可能有些丑陋,但它显然会缩短时间。请注意,对于5个输入字符串,只需将运行量变量($runqty)设置为400万,就可以为每个方法获得2000万个记录计时-请确保首先注释掉循环中的echo语句。

示例代码。。。

<?php
$input = array();
$input['my_var'] = 'foo';
$input['this.that'] = 'blah';
$input['try''me'] = 'strange';
$input['some_var_here'] = 'value';
$input['final_cut'] = 'fc99';
set_time_limit(600);
$runqty = 1;
// Method 1
$m1start = microtime(true);
for ($runs = 0; $runs < $runqty; $runs++)
{
  foreach ($input as $word => $val)
  {
    $out = strtr(ucwords(strtr($word, array('_' => ' ', '.' => '_ ', '''' => '_ '))), array(' ' => ''));
    echo "in: $word out: $out<br>'n";
  }
}
$m1stop = microtime(true);
$m1time = $m1stop - $m1start;
echo "Method 1 time: " . sprintf("%0.6f",$m1time);
echo "<br>'n";
// Method 2
$m2start = microtime(true);
for ($runs = 0; $runs < $runqty; $runs++)
{
  foreach ($input as $word => $val)
  {
    $i=0;
    $len = strlen($word);
    $ucnext = true;
    $out = '';
    while ( $i < $len )
    {
      $char = $word[$i++];
      if ( $char == '_' || $char == '.' || $char == '''' )
      {
        $ucnext = true;
        if ( $char == '.' )
          $char ='_';
        else
          $char = '';
      }
      else
      {
        if ( $ucnext )
        {
          if ( $char >= 'a' && $char <= 'z' )
            $char = ucfirst($char);
          $ucnext = false;
        }
      }
      $out .= $char;
    }
    echo "in: $word out: $out<br>'n";
  }
}
$m2stop = microtime(true);
$m2time = $m2stop - $m2start;
echo "Method 2 time: " . sprintf("%0.6f",$m2time);
echo "<br>'n";

注意:如果您不希望第一个字母大写,那么只需将ucnext变量初始化为false(而不是如图所示的true)。