php String Concatenation,Performance

Chr*_*ris 68 php string concatenation

在Java和C#等语言中,字符串是不可变的,并且一次构建一个字符串可能在计算上很昂贵.在所述语言中,有一些库类可以降低这种成本,例如C#System.Text.StringBuilder和Java java.lang.StringBuilder.

php(4或5;我对两者都感兴趣)是否共享此限制?如果是这样,是否有类似的解决方案可用?

Pet*_*ley 60

不,PHP中没有stringbuilder类的类型,因为字符串是可变的.

话虽如此,根据你正在做的事情,有不同的方法来建立一个字符串.

例如,echo将接受逗号分隔的标记以进行输出.

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

// Is the same as this
echo 'one';
echo 'two';
Run Code Online (Sandbox Code Playgroud)

这意味着您可以输出复杂的字符串而不实际使用连接,这会更慢

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

// Is faster than this...
echo 'one' . 'two';
Run Code Online (Sandbox Code Playgroud)

如果需要在变量中捕获此输出,则可以使用输出缓冲功能执行此操作.

此外,PHP的阵列性能非常好.如果你想做一个逗号分隔的值列表,只需使用implode()

$values = array( 'one', 'two', 'three' );
$valueList = implode( ', ', $values );
Run Code Online (Sandbox Code Playgroud)

最后,确保您熟悉PHP的字符串类型,它是不同的分隔符,以及每个分隔符的含义.

  • 并尽可能使用单引号. (25认同)
  • 一点神话,单引号的东西:http://nikic.github.io/2012/01/09/Disproving-the-Single-Quotes-Performance-Myth.html (16认同)
  • @gekannt因为PHP扩展/解释变量以及用双引号括起来的字符串中的额外转义序列.例如,`$ x = 5; echo"x = $ x";`将打印`x = 5`而`$ x = 5; echo'x = $ x';`将打印`x = $ x`. (4认同)

小智 28

我很好奇,所以我进行了测试.我使用了以下代码:

<?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);
Run Code Online (Sandbox Code Playgroud)

......得到了以下结果.附图.显然,就时间和内存消耗而言,sprintf是效率最低的方法.编辑:在另一个标签中查看图像,除非您有鹰视觉. 在此输入图像描述


Sea*_*ney 12

当您进行定时比较时,差异非常小,不太相关.自从选择使您的代码更易于阅读和理解之后,它会做得更多.

  • 实际上,担心这一点只是完全愚蠢,因为通常需要担心更重要的问题,例如数据库设计,大O()分析和正确的分析. (2认同)
  • 这是非常正确的,但我已经看到Java和C#中使用可变字符串类(对比s + ="blah")的情况确实显着提高了性能. (2认同)

nig*_*der 12

PHP中不需要StringBuilder模拟.

我做了几个简单的测试:

在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();
Run Code Online (Sandbox Code Playgroud)

在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();
Run Code Online (Sandbox Code Playgroud)

结果:

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

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


oss*_*sys 10

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

class StringBuilder {

  private $str = array();

  public function __construct() { }

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

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

}
Run Code Online (Sandbox Code Playgroud)

  • 好的解决方案 在`append`函数的末尾你可以添加`return $ this;`来允许方法链接:`$ sb-> append("one") - > append("two");`. (9认同)
  • ryeguy:是的,因为PHP中的字符串是可变的,这个方法是"不必要的",这个人要求对Java的StringBuilder进行类似的实现,所以在这里你去...我不会说它"显着"慢,我想你有点戏剧性.实例化管理字符串构建的类的开销可能包括成本,但可以扩展StringBuilder类的有用性以包括字符串上的其他方法.我将通过在类中实现类似的内容并尝试回发来研究实现的额外开销. (8认同)
  • 这在PHP中是完全没有必要的.事实上,我愿意打赌这比常规连接慢得多. (7认同)
  • ......他再也没有听到过. (5认同)

Jer*_*ten 6

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)
Run Code Online (Sandbox Code Playgroud)

你可以将字符附加到这样的字符串:

$string .= 'a';
Run Code Online (Sandbox Code Playgroud)