문제

에서 같은 언어 Java,C#,문자열은 변경할 수 없으며 그 계산 비용이 많이 소요될 수 있습을 구축하는 문자열을 하나의 문자니다.에서 말한 언어 있는 클래스 라이브러리를 줄이기 위해 비용 등 C# System.Text.StringBuilder Java java.lang.StringBuilder.

지 php(4 또는 5;관심이 있어요 모두에서)이 제한은?그렇다면,비슷한 문제를 해결할 방안을 마련해 사용할 수 있습니까?

도움이 되었습니까?

해결책

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

즉,하고있는 일에 따라 끈을 만드는 방법이 다릅니다.

예를 들어, 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의 배열 성능은 정말 좋습니다. 쉼표로 구분 된 값 목록과 같은 일을하고 싶다면 inmplode () 만 사용하십시오.

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

... 그리고 다음과 같은 결과를 얻었습니다. 첨부 된 이미지. 분명히 Sprintf는 시간과 메모리 소비 측면에서 가장 효율적인 방법입니다. 편집 : 독수리 비전이없는 한 다른 탭에서 이미지를 봅니다.enter image description here

을 할 때 시기 비교,차이점은 그래서 작은 그렇지 않다 매우 적합하다.그것은 만들 때문에 대한 이동하는 선택 코드를 쉽게 읽고 이해할 수 있습니다.

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

결과:

100000 반복 :
1) PHP, 일반적인 연결 : ~ 6ms
2) PHP, StringBuilder 사용 : ~ 5ms
3) C#, 일반적인 연결 : ~ 520ms
4) C#, StringBuilder 사용 : ~ 1ms

100000 반복 :
1) PHP, 일반적인 연결 : ~ 63ms
2) PHP, StringBuilder 사용 : ~ 555ms
3) C#, 일반적인 연결 : ~ 91000ms // !!!
4) C#, StringBuilder 사용 : ~ 17ms

나는 당신이 무슨 말을하는지 압니다. 방금 Java StringBuilder 클래스를 모방하기 위해이 간단한 클래스를 만들었습니다.

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';

예. 그들이하다. 예를 들어, 몇 줄을 함께 반향하려면 사용하십시오.

echo str1,str2,str3 

대신에

echo str1.str2.str3 
조금 더 빨리 얻으려면.

나는이 게시물의 끝에 코드를 작성하여 다양한 형태의 문자열 연결을 테스트했으며 실제로 메모리와 시간 발자국에서 거의 정확히 동일합니다.

내가 사용한 두 가지 주요 방법은 문자열을 서로 연결하고 배열을 문자열로 채우고이를 파괴하는 것입니다. 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 문자를 문자열에 연결하여 5 천만 바이트 (5 천만 반복)를 얻는 것은 5.97 초 만에 최대 50,322,512 바이트 (~ 48MB)의 최대 금액을 얻었습니다. 배열 방법을 수행하는 동안 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>';
}
?>

첫째, 문자열을 연결하기 위해 줄이 필요하지 않으면 그렇게하지 마십시오. 항상 더 빨리 할 것입니다.

echo $a,$b,$c;

~보다

echo $a . $b . $c;

그러나 적어도 PHP5에서 문자열 연결은 특히 주어진 문자열에 대한 참조가 하나만있는 경우 실제로 매우 빠릅니다. 통역사가 a를 사용한다고 생각합니다 StringBuilder내부적으로 기술.

PHP 문자열 내에 가변 값을 배치하는 경우 인라인 변수 포함을 사용하는 것이 약간 빠르다는 것을 이해합니다 (공식 이름이 아닙니다.

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

작업하려면 이중 인용문 안에 있어야합니다. 배열 멤버 (즉,)도 작동합니다 (즉

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

)

PHP에서 그러한 제한이 없으면 PHP는 DOT (.) 연산자와 strng를 연결할 수 있습니다.

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

출력 "Hello World"

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top