PHPstring连接,性能

在像Java和C#这样的语言中,string是不可变的,一次构build一个string可能在计算上花费很大。 在所述语言中,有一些库类可以降低成本,如C# System.Text.StringBuilder和Java java.lang.StringBuilder

是否PHP(4或5;我对这两个都感兴趣)分享这个限制? 如果是这样,是否有类似的解决scheme可用的问题?

不,PHP中没有types的stringbuilder类,因为string是可变的。

这就是说,根据你在做什么,build立一个string有不同的方法。

回声,例如,将接受逗号分隔的令牌输出。

 // This... echo 'one', 'two'; // Is the same as this echo 'one'; echo 'two'; 

这意味着你可以输出一个复杂的string,而不用实际使用连接,这会更慢

 // This... echo 'one', 'two'; // Is faster than this... echo 'one' . 'two'; 

如果你需要在一个variables中捕获这个输出,你可以使用输出缓冲function 。

另外,PHP的arrays性能非常好。 如果你想做一些像逗号分隔的值列表,只需使用implode()

 $values = array( 'one', 'two', 'three' ); $valueList = implode( ', ', $values ); 

最后,确保你熟悉PHP的stringtypes ,它是不同的分隔符,以及它们的含义。

我对此很好奇,于是我进行了testing。 我使用了下面的代码:

 <?php ini_set('memory_limit', '1024M'); define ('CORE_PATH', '/Users/foo'); define ('DS', DIRECTORY_SEPARATOR); $numtests = 1000000; function test1($numtests) { $CORE_PATH = '/Users/foo'; $DS = DIRECTORY_SEPARATOR; $a = array(); $startmem = memory_get_usage(); $a_start = microtime(true); for ($i = 0; $i < $numtests; $i++) { $a[] = sprintf('%s%sDesktop%sjunk.php', $CORE_PATH, $DS, $DS); } $a_end = microtime(true); $a_mem = memory_get_usage(); $timeused = $a_end - $a_start; $memused = $a_mem - $startmem; echo "TEST 1: sprintf()\n"; echo "TIME: {$timeused}\nMEMORY: $memused\n\n\n"; } function test2($numtests) { $CORE_PATH = '/Users/shigh'; $DS = DIRECTORY_SEPARATOR; $a = array(); $startmem = memory_get_usage(); $a_start = microtime(true); for ($i = 0; $i < $numtests; $i++) { $a[] = $CORE_PATH . $DS . 'Desktop' . $DS . 'junk.php'; } $a_end = microtime(true); $a_mem = memory_get_usage(); $timeused = $a_end - $a_start; $memused = $a_mem - $startmem; echo "TEST 2: Concatenation\n"; echo "TIME: {$timeused}\nMEMORY: $memused\n\n\n"; } function test3($numtests) { $CORE_PATH = '/Users/shigh'; $DS = DIRECTORY_SEPARATOR; $a = array(); $startmem = memory_get_usage(); $a_start = microtime(true); for ($i = 0; $i < $numtests; $i++) { ob_start(); echo $CORE_PATH,$DS,'Desktop',$DS,'junk.php'; $aa = ob_get_contents(); ob_end_clean(); $a[] = $aa; } $a_end = microtime(true); $a_mem = memory_get_usage(); $timeused = $a_end - $a_start; $memused = $a_mem - $startmem; echo "TEST 3: Buffering Method\n"; echo "TIME: {$timeused}\nMEMORY: $memused\n\n\n"; } function test4($numtests) { $CORE_PATH = '/Users/shigh'; $DS = DIRECTORY_SEPARATOR; $a = array(); $startmem = memory_get_usage(); $a_start = microtime(true); for ($i = 0; $i < $numtests; $i++) { $a[] = "{$CORE_PATH}{$DS}Desktop{$DS}junk.php"; } $a_end = microtime(true); $a_mem = memory_get_usage(); $timeused = $a_end - $a_start; $memused = $a_mem - $startmem; echo "TEST 4: Braced in-line variables\n"; echo "TIME: {$timeused}\nMEMORY: $memused\n\n\n"; } function test5($numtests) { $a = array(); $startmem = memory_get_usage(); $a_start = microtime(true); for ($i = 0; $i < $numtests; $i++) { $CORE_PATH = CORE_PATH; $DS = DIRECTORY_SEPARATOR; $a[] = "{$CORE_PATH}{$DS}Desktop{$DS}junk.php"; } $a_end = microtime(true); $a_mem = memory_get_usage(); $timeused = $a_end - $a_start; $memused = $a_mem - $startmem; echo "TEST 5: Braced inline variables with loop-level assignments\n"; echo "TIME: {$timeused}\nMEMORY: $memused\n\n\n"; } test1($numtests); test2($numtests); test3($numtests); test4($numtests); test5($numtests); 

…并得到以下结果。 附上图片。 显然,在时间和内存消耗方面,sprintf是效率最低的方法。 编辑:查看图像在另一个选项卡,除非你有鹰的视觉。 在这里输入图像描述

当你进行定时比较时,差异是如此之小,以至于不太相关。 这将使更多的select,使您的代码更容易阅读和理解。

在PHP中不需要StringBuilder模拟。

我做了几个简单的testing:

在PHP中:

 $iterations = 10000; $stringToAppend = 'TESTSTR'; $timer = new Timer(); // based on microtime() $s = ''; for($i = 0; $i < $iterations; $i++) { $s .= ($i . $stringToAppend); } $timer->VarDumpCurrentTimerValue(); $timer->Restart(); // Used purlogic's implementation. // I tried other implementations, but they are not faster $sb = new StringBuilder(); for($i = 0; $i < $iterations; $i++) { $sb->append($i); $sb->append($stringToAppend); } $ss = $sb->toString(); $timer->VarDumpCurrentTimerValue(); 

在C#(.NET 4.0)中:

 const int iterations = 10000; const string stringToAppend = "TESTSTR"; string s = ""; var timer = new Timer(); // based on StopWatch for(int i = 0; i < iterations; i++) { s += (i + stringToAppend); } timer.ShowCurrentTimerValue(); timer.Restart(); var sb = new StringBuilder(); for(int i = 0; i < iterations; i++) { sb.Append(i); sb.Append(stringToAppend); } string ss = sb.ToString(); timer.ShowCurrentTimerValue(); 

结果:

10000次迭代:
1)PHP,普通级联:〜6ms
2)PHP,使用StringBuilder:约5毫秒
3)C#,普通级联:约520ms
4)C#,使用StringBuilder:〜1ms

100000次迭代:
1)PHP,普通级联:〜63ms
2)PHP,使用StringBuilder:〜555ms
3)C#,普通级联:〜91000ms //
4)C#,使用StringBuilder:〜17ms

我知道你在说什么 我刚刚创build了这个简单的类来模拟Java StringBuilder类。

 class StringBuilder { private $str = array(); public function __construct() { } public function append($str) { $this->str[] = $str; } public function toString() { return implode($this->str); } } 

PHPstring是可变的。 你可以像这样改变特定的字符:

 $string = 'abc'; $string[2] = 'a'; // $string equals 'aba' $string[3] = 'd'; // $string equals 'abad' $string[5] = 'e'; // $string equals 'abad e' (fills character(s) in between with spaces) 

你可以像这样将字符附加到一个string中:

 $string .= 'a'; 

是。 他们是这样。 例如,如果您想要将几个string回显到一起,请使用

回声str1,str2,str3 

代替

回声str1.str2.str3 

让它快一点

首先,如果你不需要把string连接起来,不要这样做:它总是会更快

 echo $a,$b,$c; 

 echo $a . $b . $c; 

但是,至less在PHP5中,string连接真的非常快,特别是如果只有一个给定string的引用。 我想解释器在内部使用类似StringBuilder的技术。

我在这篇文章的末尾写了代码来testingstring连接的不同forms,它们在内存和时间占用上都几乎完全相等。

我使用的两个主要方法是将string连接在一起,然后用string填充数组,然后将其embedded。 我在php5.6中添加了一个1MBstring的500个string(所以结果是一个500MB的string)。 在testing的每一次迭代中,所有的内存和时间足迹都非常接近(〜$ IterationNumber * 1MB)。 两次testing的运行时间连续为50.398秒和50.843秒,这很可能在可接受的误差范围内。

垃圾收集的string不再被引用似乎是相当直接的,即使没有离开范围。 由于string是可变的,事实之后不需要额外的内存。

但是 ,下面的testing表明,在string串联时,峰值内存使用率有所不同。

 $OneMB=str_repeat('x', 1024*1024); $Final=$OneMB.$OneMB.$OneMB.$OneMB.$OneMB; print memory_get_peak_usage(); 

结果= 10,806,800字节(〜10MB w / o初始PHP内存占用)

 $OneMB=str_repeat('x', 1024*1024); $Final=implode('', Array($OneMB, $OneMB, $OneMB, $OneMB, $OneMB)); print memory_get_peak_usage(); 

结果= 6,613,320个字节(〜6MB w / o初始PHP内存占用)

所以实际上存在一个差异,在非常大的string连接中可能是重要的(在创build非常大的数据集或SQL查询时遇到这样的例子)。

但是,即使这个事实是有争议的取决于数据。 例如,将1个字符连接到一个string以获得5000万字节(如此5000万次迭代)在5.97秒内花费最多50,322,512字节(〜48MB)。 在数组方法结束时,使用7,337,107,176字节(〜6.8GB)在12.1秒内创build数组,然后花费额外的4.32秒来组合数组中的string。

Anywho …下面是我在开始时提到的基准代码,它显示了方法几乎相等。 它输出一个漂亮的HTML表格。

 <? //Please note, for the recursion test to go beyond 256, xdebug.max_nesting_level needs to be raised. You also may need to update your memory_limit depending on the number of iterations //Output the start memory print 'Start: '.memory_get_usage()."B<br><br>Below test results are in MB<br>"; //Our 1MB string global $OneMB, $NumIterations; $OneMB=str_repeat('x', 1024*1024); $NumIterations=500; //Run the tests $ConcatTest=RunTest('ConcatTest'); $ImplodeTest=RunTest('ImplodeTest'); $RecurseTest=RunTest('RecurseTest'); //Output the results in a table OutputResults( Array('ConcatTest', 'ImplodeTest', 'RecurseTest'), Array($ConcatTest, $ImplodeTest, $RecurseTest) ); //Start a test run by initializing the array that will hold the results and manipulating those results after the test is complete function RunTest($TestName) { $CurrentTestNums=Array(); $TestStartMem=memory_get_usage(); $StartTime=microtime(true); RunTestReal($TestName, $CurrentTestNums, $StrLen); $CurrentTestNums[]=memory_get_usage(); //Subtract $TestStartMem from all other numbers foreach($CurrentTestNums as &$Num) $Num-=$TestStartMem; unset($Num); $CurrentTestNums[]=$StrLen; $CurrentTestNums[]=microtime(true)-$StartTime; return $CurrentTestNums; } //Initialize the test and store the memory allocated at the end of the test, with the result function RunTestReal($TestName, &$CurrentTestNums, &$StrLen) { $R=$TestName($CurrentTestNums); $CurrentTestNums[]=memory_get_usage(); $StrLen=strlen($R); } //Concatenate 1MB string over and over onto a single string function ConcatTest(&$CurrentTestNums) { global $OneMB, $NumIterations; $Result=''; for($i=0;$i<$NumIterations;$i++) { $Result.=$OneMB; $CurrentTestNums[]=memory_get_usage(); } return $Result; } //Create an array of 1MB strings and then join w/ an implode function ImplodeTest(&$CurrentTestNums) { global $OneMB, $NumIterations; $Result=Array(); for($i=0;$i<$NumIterations;$i++) { $Result[]=$OneMB; $CurrentTestNums[]=memory_get_usage(); } return implode('', $Result); } //Recursively add strings onto each other function RecurseTest(&$CurrentTestNums, $TestNum=0) { Global $OneMB, $NumIterations; if($TestNum==$NumIterations) return ''; $NewStr=RecurseTest($CurrentTestNums, $TestNum+1).$OneMB; $CurrentTestNums[]=memory_get_usage(); return $NewStr; } //Output the results in a table function OutputResults($TestNames, $TestResults) { global $NumIterations; print '<table border=1 cellspacing=0 cellpadding=2><tr><th>Test Name</th><th>'.implode('</th><th>', $TestNames).'</th></tr>'; $FinalNames=Array('Final Result', 'Clean'); for($i=0;$i<$NumIterations+2;$i++) { $TestName=($i<$NumIterations ? $i : $FinalNames[$i-$NumIterations]); print "<tr><th>$TestName</th>"; foreach($TestResults as $TR) printf('<td>%07.4f</td>', $TR[$i]/1024/1024); print '</tr>'; } //Other result numbers print '<tr><th>Final String Size</th>'; foreach($TestResults as $TR) printf('<td>%d</td>', $TR[$NumIterations+2]); print '</tr><tr><th>Runtime</th>'; foreach($TestResults as $TR) printf('<td>%s</td>', $TR[$NumIterations+3]); print '</tr></table>'; } ?> 

如果你将variables值放在PHPstring中,我知道使用内联variables包含会稍微快一些(这不是正式名称 – 我不记得是什么)

 $aString = 'oranges'; $compareString = "comparing apples to {$aString}!"; echo $compareString comparing apples to oranges! 

必须在双引号内工作。 也适用于数组成员(即

 echo "You requested page id {$_POST['id']}"; 

在PHP中没有这样的限制,PHP可以连接点(。)运算符

 $a="hello "; $b="world"; echo $a.$b; 

输出“你好世界”