Ich habe den Code am Ende dieses Beitrags geschrieben, um die verschiedenen Formen der Zeichenfolgenverkettung zu testen, und sie sind wirklich alle in Bezug auf Speicher- und Zeitabdruck fast genau gleich.
Die beiden wichtigsten Methoden, die ich verwendet habe, bestehen darin, Zeichenfolgen miteinander zu verknüpfen, ein Array mit Zeichenfolgen zu füllen und diese dann zu implodieren. Ich habe 500 Zeichenfolgen mit einer 1-MB-Zeichenfolge in PHP 5.6 hinzugefügt (das Ergebnis ist also eine 500-MB-Zeichenfolge). Bei jeder Iteration des Tests waren alle Speicher- und Zeitabdrücke sehr, sehr eng (bei ~ $ IterationNumber * 1MB). Die Laufzeit beider Tests betrug nacheinander 50,398 Sekunden und 50,843 Sekunden, was höchstwahrscheinlich innerhalb akzeptabler Fehlergrenzen liegt.
Die Speicherbereinigung von Zeichenfolgen, auf die nicht mehr verwiesen wird, scheint ziemlich unmittelbar zu sein, auch ohne jemals den Gültigkeitsbereich zu verlassen. Da die Zeichenfolgen veränderbar sind, wird nachträglich kein zusätzlicher Speicher benötigt.
Die folgenden Tests haben jedoch gezeigt, dass sich die maximale Speichernutzung unterscheidet, während die Zeichenfolgen verkettet werden.
$OneMB=str_repeat('x', 1024*1024);
$Final=$OneMB.$OneMB.$OneMB.$OneMB.$OneMB;
print memory_get_peak_usage();
Ergebnis = 10.806.800 Bytes (~ 10 MB ohne den anfänglichen PHP-Speicherbedarf)
$OneMB=str_repeat('x', 1024*1024);
$Final=implode('', Array($OneMB, $OneMB, $OneMB, $OneMB, $OneMB));
print memory_get_peak_usage();
Ergebnis = 6.613.320 Bytes (~ 6 MB ohne den anfänglichen PHP-Speicherbedarf)
Es gibt also tatsächlich einen Unterschied, der bei sehr sehr großen Zeichenfolgenverkettungen in Bezug auf den Speicher von Bedeutung sein kann (ich habe solche Beispiele beim Erstellen sehr großer Datenmengen oder SQL-Abfragen gefunden).
Aber auch diese Tatsache ist je nach Daten umstritten. Das Verketten eines Zeichens mit einer Zeichenfolge, um 50 Millionen Bytes (also 50 Millionen Iterationen) zu erhalten, dauerte beispielsweise in 5,97 Sekunden maximal 50.322.512 Bytes (~ 48 MB). Dabei wurden für die Array-Methode 7.337.107.176 Byte (~ 6,8 GB) verwendet, um das Array in 12,1 Sekunden zu erstellen. Anschließend wurden zusätzliche 4,32 Sekunden benötigt, um die Zeichenfolgen aus dem Array zu kombinieren.
Egal ... das Folgende ist der Benchmark-Code, den ich am Anfang erwähnt habe und der zeigt, dass die Methoden ziemlich gleich sind. Es gibt eine hübsche HTML-Tabelle aus.
<?
print 'Start: '.memory_get_usage()."B<br><br>Below test results are in MB<br>";
global $OneMB, $NumIterations;
$OneMB=str_repeat('x', 1024*1024);
$NumIterations=500;
$ConcatTest=RunTest('ConcatTest');
$ImplodeTest=RunTest('ImplodeTest');
$RecurseTest=RunTest('RecurseTest');
OutputResults(
Array('ConcatTest', 'ImplodeTest', 'RecurseTest'),
Array($ConcatTest, $ImplodeTest, $RecurseTest)
);
function RunTest($TestName)
{
$CurrentTestNums=Array();
$TestStartMem=memory_get_usage();
$StartTime=microtime(true);
RunTestReal($TestName, $CurrentTestNums, $StrLen);
$CurrentTestNums[]=memory_get_usage();
foreach($CurrentTestNums as &$Num)
$Num-=$TestStartMem;
unset($Num);
$CurrentTestNums[]=$StrLen;
$CurrentTestNums[]=microtime(true)-$StartTime;
return $CurrentTestNums;
}
function RunTestReal($TestName, &$CurrentTestNums, &$StrLen)
{
$R=$TestName($CurrentTestNums);
$CurrentTestNums[]=memory_get_usage();
$StrLen=strlen($R);
}
function ConcatTest(&$CurrentTestNums)
{
global $OneMB, $NumIterations;
$Result='';
for($i=0;$i<$NumIterations;$i++)
{
$Result.=$OneMB;
$CurrentTestNums[]=memory_get_usage();
}
return $Result;
}
function ImplodeTest(&$CurrentTestNums)
{
global $OneMB, $NumIterations;
$Result=Array();
for($i=0;$i<$NumIterations;$i++)
{
$Result[]=$OneMB;
$CurrentTestNums[]=memory_get_usage();
}
return implode('', $Result);
}
function RecurseTest(&$CurrentTestNums, $TestNum=0)
{
Global $OneMB, $NumIterations;
if($TestNum==$NumIterations)
return '';
$NewStr=RecurseTest($CurrentTestNums, $TestNum+1).$OneMB;
$CurrentTestNums[]=memory_get_usage();
return $NewStr;
}
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>';
}
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>';
}
?>