source

php 문자열 연결, 성능

factcode 2022. 9. 23. 00:03
반응형

php 문자열 연결, 성능

Java 및 C#과 같은 언어에서는 문자열은 불변하며 한 번에 한 문자를 작성하는 데 계산 비용이 많이 듭니다.언어에는 이 비용을 하기 위한 .를 들어 C#은 C#으로 되어 있습니다.System.Text.StringBuilder Java » Javajava.lang.StringBuilder.

php(4 또는 5; 둘 다 관심 있음)는 이 제한을 공유합니까?그렇다면 이 문제에 대한 유사한 해결 방법이 있습니까?

아니요, 문자열이 변경 가능하기 때문에 PHP에는 문자열 빌더 클래스의 유형이 없습니다.

말하자면, 어떤 일을 하느냐에 따라 끈을 만드는 방법이 다릅니다.

예를 들어 echo는 출력용으로 쉼표로 구분된 토큰을 받아들입니다.

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

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

즉, 실제로는 연결을 사용하지 않고도 복잡한 문자열을 출력할 수 있으며, 이는 속도가 느려집니다.

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

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

이 출력을 변수에 캡처해야 하는 경우 출력 버퍼링 함수를 사용하여 캡처할 수 있습니다.

또, PHP의 어레이 퍼포먼스는 매우 양호합니다.쉼표로 구분된 값 목록과 같은 작업을 수행하려면 innode()를 사용하십시오.

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

마지막으로, PHP의 문자열 유형과 구분 기호 및 각 문자열의 의미를 숙지해야 합니다.

저는 이게 궁금해서 테스트를 해봤어요.다음 코드를 사용했습니다.

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

결과를 ...리음음 음 음 음 、 음 음 음 、 음 음 다 、이미지를 첨부했습니다.: 독수리 없는 한 편집: 독수리 시야가 없는 한 다른 탭에서 이미지를 봅니다. 여기에 이미지 설명 입력

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

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

결과:

반복 명령어:
PHP, ~) PHP, 반ms: 6 66ms
PHP, Builder ~5) PHP, String Builder 사용: 5 55 ms
~ ms3) C#, 밀밀 : 520 ms 。
C String Builder ~) C#, String Builder 사용: 1ms

반복 100,000회 반복:
PHP, ~ )PHP, 범한 php~~ :~63ms 。
PHP, Builder ~)PHP, String Builder는 ~ 555ms입니다.
C ~3) C#, ,,~ : ~91000ms //!!
C String Builder 4) C#, String Builder 사용: ~17ms.

언제 주어진 시간 안에 비교 하면 그 차이들이 너무는 그것이 매우 적절하지 않다 작다.그것은 이후 코드를 읽고 이해하기 더 쉽게 선택을 더 가게 할 것이다.

네가 무슨 말하는지 알아.Java String Builder 클래스를 에뮬레이트하기 위해 이 단순한 클래스를 만들었습니다.

class StringBuilder {

  private $str = array();

  public function __construct() { }

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

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

}

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)

문자열에 다음과 같은 문자를 추가할 수 있습니다.

$string .= 'a';

저는 이 글의 끝에 다양한 형태의 스트링 연결을 테스트하기 위해 코드를 작성했습니다.그것들은 메모리와 시간의 풋프린트에서 거의 정확하게 일치합니다.

제가 사용한 두 가지 주요 방법은 문자열을 서로 연결하고 배열에 문자열을 채운 다음 삽입하는 것입니다.php 5.6에서 1MB 스트링으로 500개의 스트링을 추가했습니다(따라서 500MB 스트링).테스트를 반복할 때마다 모든 메모리 및 시간 풋프린트는 매우 가까웠습니다(약 $IterationNumber*1MB).두 테스트의 실행 시간은 50.398초와 50.843초로, 허용 오차 범위 내에 있을 가능성이 높습니다.

더 이상 참조되지 않는 문자열의 가비지 컬렉션은 범위를 벗어나지 않아도 매우 신속하게 수집될 수 있습니다.문자열은 변경 가능하기 때문에 그 이후에는 추가 메모리가 필요하지 않습니다.

다만, 다음의 테스트에서는, 문자열의 접속중에 피크 메모리 사용량에 차이가 있는 것을 알 수 있었습니다.

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

결과=10,806,800 바이트(초기 PHP 메모리 설치 공간 포함 시 최대 10MB)

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

결과=6,613,320바이트(초기 PHP 메모리 설치 공간 포함 시 최대 6MB)

따라서 메모리 측면에서 매우 큰 문자열 연결에는 실제로 큰 차이가 있을 수 있습니다(매우 큰 데이터 세트 또는 SQL 쿼리를 생성할 때 이러한 예가 있습니다).

그러나 이 사실조차도 데이터에 따라서는 논란의 여지가 있다.예를 들어 문자열에 1개의 문자를 연결하여 5000만 바이트(따라서 5000만 번 반복)를 얻으려면 5.97초 동안 최대 50,322,512 바이트(~48 MB)가 소요됩니다.어레이 방식을 실행하는 동안 7,337,107,176바이트(~6.8GB)를 사용하여 어레이를 12.1초 만에 만들고 어레이에서 문자열을 조합하는 데 4.32초가 더 걸렸습니다.

누구라도...아래는 처음에 말씀드린 벤치마크 코드이며, 두 방법이 거의 동일하다는 것을 보여줍니다.예쁜 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>';
}
?>

네, 그래요예를 들어, 몇 개의 스트링을 함께 에코하려면

에코 str1, str2, str3

대신

echo str1.str2.str3
to get it a little faster.

방금 이 문제를 발견했습니다.

$str .= '문자열 연결.';

대.

$str = $str . '문자열 연결.';

지금까지 아무도 이걸 비교하지 못한 것 같아.결과는 50.000회 반복과 php 7.4로 매우 이상하다.

문자열 1: 0.001391887647949

문자열 2: 1.1183910369873

Faktor: 803!!

$currentTime = microtime(true);
$str = '';
for ($i = 50000; $i > 0; $i--) {
    $str .= 'String concatenation. ';
}
$currentTime2 = microtime(true);
echo "String 1: " . ( $currentTime2 - $currentTime);

$str = '';
for ($i = 50000; $i > 0; $i--) {
    $str = $str . 'String concatenation. ';
}
$currentTime3 = microtime(true);
echo "<br>String 2: " . ($currentTime3 - $currentTime2);

echo "<br><br>Faktor: " . (($currentTime3 - $currentTime2) / ( $currentTime2 - $currentTime));

누가 확인 좀 해줄래?큰 파일에서 몇 줄을 읽고 원하는 줄만 다시 문자열에 붙여서 삭제하고 있었기 때문에 이렇게 되었습니다.

=를 사용하면 모든 문제를 해결할 수 있었습니다.타임아웃 하기 전에!

첫째, 스트링을 연결할 필요가 없는 경우 이 작업을 수행하지 마십시오. 이 작업을 수행하는 것이 항상 더 빠릅니다.

echo $a,$b,$c;

보다

echo $a . $b . $c;

그러나 적어도 PHP5에서는 문자열 연결이 매우 빠릅니다. 특히 특정 문자열에 대한 참조가 하나만 있는 경우에는 더욱 그렇습니다.통역사가 쓰는 게StringBuilder- 내부적으로는 테크닉과 비슷합니다.

변수 값을 PHP 문자열 내에 배치하는 경우 인라인 변수 포함을 사용하는 것이 약간 빠르다는 것을 이해합니다(공식 이름이 아닙니다.무엇이 기억나지 않습니다).

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

작업하려면 큰따옴표 안에 있어야 합니다.어레이 멤버에게도 동작합니다(즉,

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

)

php에는 이러한 제한이 없습니다.php는 string을 dot() 연산자와 연결할 수 있습니다.

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

출력 "hello world"

언급URL : https://stackoverflow.com/questions/124067/php-string-concatenation-performance

반응형