72 votes

php string concaténation, performance

Dans des langages tels que Java et C #, les chaînes sont immuables et il peut être coûteux en calculs de créer une chaîne un caractère à la fois. Dans ces langages, il existe des classes de bibliothèque pour réduire ce coût, telles que C # System.Text.StringBuilder et Java java.lang.StringBuilder .

Est-ce que php (4 ou 5; les deux m'intéressent) partage cette limitation? Si oui, existe-t-il des solutions similaires au problème?

63voto

Peter Bailey Points 62125

Non, il n'y a pas de type de la classe stringbuilder en PHP, depuis les chaînes de caractères sont mutables.

Cela étant dit, il y a différentes façons de construire une chaîne de caractères, en fonction de ce que vous faites.

echo, par exemple, accepte séparée par des virgules des jetons pour la sortie.

// This...
echo 'one', 'two';

// Is the same as this
echo 'one';
echo 'two';

Ce que cela signifie est que vous pouvez obtenir en sortie une chaîne complexe sans utiliser la concaténation, qui serait plus lent

// This...
echo 'one', 'two';

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

Si vous avez besoin pour capturer cette sortie dans une variable, vous pouvez le faire avec le tampon de sortie fonctions.

Aussi, PHP sur les performances de l'ensemble est vraiment bon. Si vous voulez faire quelque chose comme une liste séparée par des virgules de valeurs, il suffit d'utiliser implode()

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

Enfin, assurez-vous de vous familiariser avec PHP du type de chaîne et c'est différent des délimiteurs et les implications de chacun.

33voto

Evilnode Points 93

J'étais curieux à ce sujet, alors j'ai fait un test. J'ai utilisé le code suivant:

 <?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);
 

... et obtenu les résultats suivants. Image jointe. De toute évidence, sprintf est le moyen le moins efficace, à la fois en termes de temps et de consommation de mémoire. EDIT: affiche l'image dans un autre onglet sauf si vous avez une vision d'aigle. entrez la description de l'image ici

16voto

nightcoder Points 4604

StringBuilder analogique n'est pas nécessaire en PHP.

J'ai fait quelques tests simples:

en 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();

en 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();

Résultats:

10000 itérations:
1) de PHP, ordinaire de concaténation: ~6ms
2) de PHP, à l'aide de StringBuilder: ~5 ms
3) C#, ordinaire de concaténation: ~520ms
4) C#, à l'aide de StringBuilder: ~1ms

100000 itérations:
1) de PHP, ordinaire de concaténation: ~63ms
2) de PHP, à l'aide de StringBuilder: ~555ms
3) C#, ordinaire de concaténation: ~91000ms // !!!
4) C#, à l'aide de StringBuilder: ~17ms

12voto

SeanDowney Points 5299

Lorsque vous effectuez une comparaison chronométrée, les différences sont si minimes qu’elles ne sont pas très pertinentes. Cela ferait plus puisque choisir pour rendre votre code plus facile à lire et à comprendre.

10voto

ossys Points 1618

Je sais de quoi tu parles. Je viens de créer cette classe simple pour émuler la classe Java StringBuilder.

 class StringBuilder {

private $str = array();

public function __construct() { }

public function append($str) {
    $this->str[] = $str;
}

public function toString() {
    return implode($this->str);
}
 

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X