php 字符串连接,性能
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/124067/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
php String Concatenation, Performance
提问by Chris
In languages like Java and C#, strings are immutable and it can be computationally expensive to build a string one character at a time. In said languages, there are library classes to reduce this cost such as C# System.Text.StringBuilderand Java java.lang.StringBuilder.
在 Java 和 C# 等语言中,字符串是不可变的,一次一个字符构建一个字符串的计算成本可能很高。在上述语言中,有一些库类可以降低这种成本,例如 C#System.Text.StringBuilder和 Java java.lang.StringBuilder。
Does php (4 or 5; I'm interested in both) share this limitation? If so, are there similar solutions to the problem available?
php(4 或 5;我对两者都感兴趣)是否共享此限制?如果是这样,是否有类似的问题可用的解决方案?
采纳答案by Peter Bailey
No, there is no type of stringbuilder class in PHP, since strings are mutable.
不,PHP 中没有 stringbuilder 类的类型,因为字符串是可变的。
That being said, there are different ways of building a string, depending on what you're doing.
话虽如此,根据您在做什么,有多种构建字符串的方法。
echo, for example, will accept comma-separated tokens for output.
例如,echo 将接受逗号分隔的标记用于输出。
// This...
echo 'one', 'two';
// Is the same as this
echo 'one';
echo 'two';
What this means is that you can output a complex string without actually using concatenation, which would be slower
这意味着您可以在不实际使用连接的情况下输出复杂的字符串,这会更慢
// This...
echo 'one', 'two';
// Is faster than this...
echo 'one' . 'two';
If you need to capture this output in a variable, you can do that with the output buffering functions.
如果您需要在变量中捕获此输出,您可以使用输出缓冲函数来实现。
Also, PHP's array performance is really good. If you want to do something like a comma-separated list of values, just use implode()
另外,PHP 的数组性能确实不错。如果你想做一个逗号分隔的值列表之类的事情,只需使用 implode()
$values = array( 'one', 'two', 'three' );
$valueList = implode( ', ', $values );
Lastly, make sure you familiarize yourself with PHP's string typeand it's different delimiters, and the implications of each.
最后,请确保您熟悉PHP 的字符串类型及其不同的分隔符,以及每个的含义。
回答by Evilnode
I was curious about this, so I ran a test. I used the following code:
我对这个很好奇,所以我做了一个测试。我使用了以下代码:
<?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);
...
And got the following results. Image attached. Clearly, sprintf is the least efficient way to do it, both in terms of time and memory consumption.
EDIT: view image in another tab unless you have eagle vision.

...并得到以下结果。附上图片。显然,sprintf 是效率最低的方法,无论是在时间还是内存消耗方面。编辑:除非您有鹰眼,否则请在另一个选项卡中查看图像。

回答by nightcoder
StringBuilder analog is not needed in PHP.
PHP 中不需要 StringBuilder 模拟。
I made a couple of simple tests:
我做了几个简单的测试:
in PHP:
在 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();
in C# (.NET 4.0):
在 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();
Results:
结果:
10000 iterations:
1) PHP, ordinary concatenation: ~6ms
2) PHP, using StringBuilder: ~5 ms
3) C#, ordinary concatenation: ~520ms
4) C#, using StringBuilder: ~1ms
10000 次迭代:
1) PHP,普通串联:~6ms
2) PHP,使用 StringBuilder:~5 ms
3) C#,普通串联:~520ms
4) C#,使用 StringBuilder:~1ms
100000 iterations:
1) PHP, ordinary concatenation: ~63ms
2) PHP, using StringBuilder: ~555ms
3) C#, ordinary concatenation: ~91000ms // !!!
4) C#, using StringBuilder: ~17ms
100000 次迭代:
1) PHP,普通连接:~63ms
2) PHP,使用 StringBuilder:~555ms
3) C#,普通连接:~91000ms // !!!
4) C#,使用 StringBuilder:~17ms
回答by SeanDowney
When you do a timed comparison, the differences are so small that it isn't very relevant. It would make more since to go for the choice that makes your code easier to read and understand.
当您进行定时比较时,差异非常小,以至于不是很相关。选择使您的代码更易于阅读和理解的选择会更有意义。
回答by ossys
I know what you're talking about. I just created this simple class to emulate the Java StringBuilder class.
我知道你在说什么。我刚刚创建了这个简单的类来模拟 Java StringBuilder 类。
class StringBuilder {
private $str = array();
public function __construct() { }
public function append($str) {
$this->str[] = $str;
}
public function toString() {
return implode($this->str);
}
}
回答by Jeremy Ruten
PHP strings are mutable. You can change specific characters like this:
PHP 字符串是可变的。您可以像这样更改特定字符:
$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)
And you can append characters to a string like this:
您可以将字符附加到字符串中,如下所示:
$string .= 'a';
回答by mixdev
Yes. They do. For e.g., if you want to echo couple of strings together, use
是的。他们是这样。例如,如果您想将几个字符串回显在一起,请使用
echo str1,str2,str3
instead of
代替
echo str1.str2.str3让它快一点。
回答by Dakusan
I wrote the code at the end of this post to test the different forms of string concatenation and they really are all almost exactly equal in both memory and time footprints.
我在这篇文章的末尾编写了代码来测试不同形式的字符串连接,它们在内存和时间足迹方面几乎完全相同。
The two primary methods I used are concatenating strings onto each other, and filling an array with strings and then imploding them. I did 500 string additions with a 1MB string in php 5.6 (so the result is a 500MB string). At every iteration of the test, all memory and time footprints were very very close (at ~$IterationNumber*1MB). The runtime of both tests was 50.398 seconds and 50.843 seconds consecutively which are most likely within acceptable margins of error.
我使用的两种主要方法是将字符串相互连接,并用字符串填充数组,然后将它们内爆。我在 php 5.6 中用 1MB 的字符串添加了 500 个字符串(所以结果是 500MB 的字符串)。在测试的每次迭代中,所有内存和时间占用都非常接近(在 ~$IterationNumber*1MB)。两个测试的运行时间连续为 50.398 秒和 50.843 秒,这很可能在可接受的误差范围内。
Garbage collection of strings that are no longer referenced seems to be pretty immediate, even without ever leaving the scope. Since the strings are mutable, no extra memory is really required after the fact.
不再引用的字符串的垃圾收集似乎非常直接,即使从未离开范围。由于字符串是可变的,因此实际上不需要额外的内存。
HOWEVER, The following tests showed that there is a different in peak memory usage WHILEthe strings are being concatenated.
无论其,下面的测试表明,在峰值内存使用量的不同WHILE琴弦被连接起来。
$OneMB=str_repeat('x', 1024*1024);
$Final=$OneMB.$OneMB.$OneMB.$OneMB.$OneMB;
print memory_get_peak_usage();
Result=10,806,800 bytes (~10MB w/o the initial PHP memory footprint)
结果=10,806,800 字节(约 10MB,不含初始 PHP 内存占用)
$OneMB=str_repeat('x', 1024*1024);
$Final=implode('', Array($OneMB, $OneMB, $OneMB, $OneMB, $OneMB));
print memory_get_peak_usage();
Result=6,613,320 bytes (~6MB w/o the initial PHP memory footprint)
结果=6,613,320 字节(约 6MB,不含初始 PHP 内存占用)
So there is in fact a difference that could be significant in very very large string concatenations memory-wise (I have run into such examples when creating very large data sets or SQL queries).
因此,实际上在非常非常大的字符串连接内存方面存在显着差异(我在创建非常大的数据集或 SQL 查询时遇到过这样的例子)。
But even this fact is disputable depending upon the data. For example, concatenating 1 character onto a string to get 50 million bytes (so 50 million iterations) took a maximum amount of 50,322,512 bytes (~48MB) in 5.97 seconds. While doing the array method ended up using 7,337,107,176 bytes (~6.8GB) to create the array in 12.1 seconds, and then took an extra 4.32 seconds to combine the strings from the array.
但即使是这个事实也是有争议的,这取决于数据。例如,将 1 个字符连接到一个字符串上以获得 5000 万字节(即 5000 万次迭代)在 5.97 秒内最多需要 50,322,512 字节(~48MB)。在执行数组方法时,最终使用 7,337,107,176 字节(~6.8GB)在 12.1 秒内创建数组,然后额外花费 4.32 秒来组合数组中的字符串。
Anywho... the below is the benchmark code I mentioned at the beginning which shows the methods are pretty much equal. It outputs a pretty HTML table.
任何人...下面是我在开头提到的基准代码,它显示方法几乎相同。它输出一个漂亮的 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>';
}
?>
回答by Anthony Williams
Firstly, if you don't need the strings to be concatenated, don't do it: it will always be quicker to do
首先,如果您不需要连接字符串,请不要这样做:这样做总是会更快
echo $a,$b,$c;
than
比
echo $a . $b . $c;
However, at least in PHP5, string concatenation is really quite fast, especially if there's only one reference to a given string. I guess the interpreter uses a StringBuilder-like technique internally.
但是,至少在 PHP5 中,字符串连接确实非常快,尤其是当给定字符串只有一个引用时。我猜解释器在StringBuilder内部使用了类似技术。
回答by cori
If you're placing variable values within PHP strings, I understand that it's slightly quicker to use in-line variable inclusion (that's not it's official name - I can't remember what is)
如果您在 PHP 字符串中放置变量值,我知道使用内联变量包含会稍微快一点(这不是官方名称 - 我不记得是什么)
$aString = 'oranges';
$compareString = "comparing apples to {$aString}!";
echo $compareString
comparing apples to oranges!
Must be inside double-quotes to work. Also works for array members (i.e.
必须在双引号内才能工作。也适用于数组成员(即
echo "You requested page id {$_POST['id']}";
)
)

