steffi: hilfe! php wird immer langsamer

hi.
hab nun lange versucht, herauszufinden, warum eines meiner import-scripte immer langsamer wird. nun scheine ich das time lag gefunden zu haben. es heißt str_replace(array())!
der erste durchgang geht schnell von statten. aber dann dauert es manchmal sogar 10x länger!
hierzu das schlampig geschriebene demonstrationsbeispiel:

<?
set_time_limit(0);
$ts_=0;
$testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
$testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
$testa[]='ccccccccccccccccccccccccc';
$testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
$testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
$testa[]='ccccccccccccccccccccccccc';
$testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
$testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
$testa[]='ccccccccccccccccccccccccc';
$testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
$testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
$testa[]='ccccccccccccccccccccccccc';
for($i=0;$i<10000;$i++)
{
 $ts=time();echo ".";flush();
 for($j=0;$j<1000;$j++)
  $test=str_replace('aaa','bbb',$testa);
 $ts_=$ts_+(time()-$ts);
 $ts=time();
 if(bcmod($i,100)==0)
 {
  echo '<br>'.$ts_.'<br>';
  $ts_=0;
 }
}
?>

was kann ich tun, damit jeder durchgang so schnell geht wie der erste?

danke

  1. Hallo

    str_replace(array())

    str_replace ist eine _Stringfunktion_.

    der erste durchgang geht schnell von statten. aber dann dauert es manchmal sogar 10x länger!

    $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
    $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
    $testa[]='ccccccccccccccccccccccccc';
    for($i=0;$i<10000;$i++)
    {
    $ts=time();echo ".";flush();
    for($j=0;$j<1000;$j++)
      $test=str_replace('aaa','bbb',$testa);

    was kann ich tun, damit jeder durchgang so schnell geht wie der erste?

    Die Funktion str_replace auf einen String und nicht auf das _ganze_ Array ($testa) anwenden.

    Tschö, Auge

    --
    Die Musik drückt aus, was nicht gesagt werden kann und worüber es unmöglich ist zu schweigen.
    (Victor Hugo)
    Veranstaltungsdatenbank Vdb 0.1
    1. Hallo Auge.

      Die Funktion str_replace auf einen String und nicht auf das _ganze_ Array ($testa) anwenden.

      Was aber zumindest seit PHP 4.0.5 möglich ist:

      As of PHP 4.0.5, every parameter in str_replace() can be an array.

      Einen schönen Dienstag noch.

      Gruß, Ashura

      --
      sh:( fo:} ch:? rl:( br: n4:~ ie:{ mo:| va:) de:> zu:} fl:( ss:) ls:[ js:|
      „It is required that HTML be a common language between all platforms. This implies no device-specific markup, or anything which requires control over fonts or colors, for example. This is in keeping with the SGML ideal.“
      [HTML Design Constraints: Logical Markup]
      1. und wie das möglich ist. und es ist (eigentlich) wesentlich schneller als
        foreach($testa AS $testname=>$testvalue)
         $testa[$testname]=str_replace('_aaa','_bbb',$testvalue);
        aber das tut nichts zur sache.
        weiß denn hier auch keiner, wie das pgänomen zu erklären ist, warum das so langsam wird und beim ersten mal nicht?

      2. Hallo

        Was aber zumindest seit PHP 4.0.5 möglich ist:

        As of PHP 4.0.5, every parameter in str_replace() can be an array.

        Gut, hätte mal vorher nachschauen sollen. Gesagt, getun getan getätet ...

        Da steht auch:

        If subject is an array, then the search and replace is performed with every entry of subject, and the return value is an array as well.

        Das trifft auf diesen Fall zu. Allerdings wird das durch die Schleifen unnötig oft getan. Wenn str-replace von sich aus das veränderte Array zurückgibt, ist die Ursache für das Einschlafen wohl in der unnötigen Schleife zu suchen. Ich zitiere mal:

        for($i=0;$i<10000;$i++)  
           {  
           $ts=time();echo ".";flush();  
           for($j=0;$j<1000;$j++)  
           $test=str_replace('aaa','bbb',$testa);  
           $ts_=$ts_+(time()-$ts);  
           $ts=time();  
           if (bcmod($i,100)==0)  
              {  
              echo '<br>'.$ts_.'<br>';  
              $ts_=0;  
              }  
           }
        

        10.000 (in Worten: zehntausend) mal wird die Funktion str_replace aufgerufen, um 'aaa' durch 'bbb' zu ersetzen. Sollte man sich da nicht schonmal Gedanken um den zugewiesenen Speicher gemacht haben?

        Außerdem steckt da noch eine Schleife drin (for($j=0;$j<1000;$j++)), die ohne Anweisung bleibt.

        Tschö, Auge

        --
        Die Musik drückt aus, was nicht gesagt werden kann und worüber es unmöglich ist zu schweigen.
        (Victor Hugo)
        Veranstaltungsdatenbank Vdb 0.1
        1. ob das script sinn macht, ist ja nicht der springende punkt.
          es soll ja nur gezeigt werden, warum ich zu der aussage komme, dass str_replace mein script (nein, nicht dieses - ein anderes... aber das genannte auch!) bremst.
          hab von garbage collector gehört, der scheinbar anspringt bei der ausführung des scriptes.
          aber ich frage mich oder besser euch, warum er das beim 2., 3. n-ten mal tut aber nicht beim ERSTEN mal. denn beim ersten durchlauf ist das script ja wie gesagt extrem schnell. ab dann nicht mehr.
          ok, wenn es ein memory usage problem ist, dann brauch ich irgendne lösung, um den cache, den die str_replace funtion in anspruch nimmt, zu leeren vor/nach ausführung des befehls.
          aber wie?

          1. Hallo,

            vielleicht wäre es besser, wenn man sich das eigentliche Script ansehen könnte.

            Gruß aus Berlin!
            eddi

        2. Liebes Auge,

          Du sagtest etwas zu diesem Code:

          for($i=0;$i<10000;$i++)

          {
             $ts=time();echo ".";flush();
             for($j=0;$j<1000;$j++)
             $test=str_replace('aaa','bbb',$testa);
             $ts_=$ts_+(time()-$ts);
             $ts=time();
             if (bcmod($i,100)==0)
                {
                echo '<br>'.$ts_.'<br>';
                $ts_=0;
                }
             }

          
          >   
          > Außerdem steckt da noch eine Schleife drin (`for($j=0;$j<1000;$j++)`{:.language-php}), die ohne Anweisung bleibt.  
            
          ich möchte Dir hier widersprechen: for (...) Anweisung; bewirkt, dass "Anweisung" in jedem for-Schleifen-Durchlauf ausgeführt wird. Es braucht bei einer einzigen Anweisung keine geschweiften Klammern wie bei einem ganzen Anweisungs\_block\_. Deshalb wird in jedem der 10000 (zehnTAUSEND) Schleifendurchgänge (mit $i als Index) 1000x (tausendmal - mit $j als Index) die str\_replace-Funktion ausgeführt, sodass wir auf die lächerliche Summe von 10 000 000 (zehn Millionen) str\_replace-Ausführungen kommen (und die auf ein Array!).  
            
          Ich schreibe einmal obigen Code mit anderen Einrückungen, dann wird es besser sichtbar:  
            
          ~~~php
          for($i=0;$i<10000;$i++)  
              {  
              $ts=time();echo ".";flush();  
            
              for($j=0;$j<1000;$j++)  
                 $test=str_replace('aaa','bbb',$testa);  
            
              $ts_=$ts_+(time()-$ts);  
              $ts=time();  
              if (bcmod($i,100)==0)  
                 {  
                 echo '<br>'.$ts_.'<br>';  
                 $ts_=0;  
                 }  
              }
          

          Auch ich frage mich, wie jetzt da der Sinn erkennbar werden soll...

          Liebe Grüße aus Ellwangen,

          Felix Riesterer.

          1. Hallo

            Liebes Auge,

            Du sagtest etwas zu diesem Code:

            for($i=0;$i<10000;$i++)

            {
               ...
               }

            
            > >   
            > > Außerdem steckt da noch eine Schleife drin (`for ($j=0;$j<1000;$j++)`{:.language-php}), die ohne Anweisung bleibt.  
            >   
            > ich möchte Dir hier widersprechen: for (...) Anweisung; bewirkt, dass "Anweisung" in jedem for-Schleifen-Durchlauf ausgeführt wird. Es braucht bei einer einzigen Anweisung keine geschweiften Klammern wie bei einem ganzen Anweisungs\_block\_.  
              
            Aha. Dass das auch ohne Block oder andere Markierungen (`for(...): ... endfor;`{:.language-php}) funktioniert, wusste ich nicht.  
              
            
            > Deshalb wird in jedem der 10000 (zehnTAUSEND) Schleifendurchgänge (mit $i als Index) 1000x (tausendmal - mit $j als Index) die str\_replace-Funktion ausgeführt, sodass wir auf die lächerliche Summe von 10 000 000 (zehn Millionen) str\_replace-Ausführungen kommen (und die auf ein Array!).  
              
            Bevor ich entdeckte, dass das von mir als zwingend vorausgesetzte Klammerpaar fehlte, war ich auch schon bei dieser Zahl. :-)  
              
            
            > Auch ich frage mich, wie jetzt da der Sinn erkennbar werden soll...  
              
            Offensichtlich eine [Demo](https://forum.selfhtml.org/?t=130803&m=845863).  
              
            Tschö, Auge  
            
            -- 
            Die Musik drückt aus, was nicht gesagt werden kann und worüber es unmöglich ist zu schweigen.  
            (Victor Hugo)  
              
            [Veranstaltungsdatenbank Vdb 0.1](http://termindbase.auge8472.de/)
            
  2. Hallo,

    Du wirst nichts schnelleres in PHP finden als str_replace(). Zu Deinem Testscript ist erstmal soweit nichts zu sagen, als das zehnmillionenmal eine Funktion aufgerufen wird. Dies dauert selbstverständlich.
     Inwiefern die darin ermittelte Zeit eine Relevanz in Dein importscript hat, läßt sich von unserer Warte aus nicht überprüfen.

    Gruß aus Berlin!
    eddi

    1. hier nochmal das script und unten das ergebnis auf meinem server.
      habe festgestellt, dass das nicht auf jedem server so ist.
      fall jemand weiß, welche einstellung schuld daran hat -> bitte bescheid sagen: garbage collector memory limit hab ich mir sagen lassen. aber ka, wie mans umstellt...

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24

      <?
          $testa = array();
          $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
          $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
          $testa[]='ccccccccccccccccccccccccc';
          $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
          $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
          $testa[]='ccccccccccccccccccccccccc';
          $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
          $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
          $testa[]='ccccccccccccccccccccccccc';
          $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
          $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
          $testa[]='ccccccccccccccccccccccccc';
          echo memory_get_usage()."<- memory usage before starting loop with limit at: ".ini_get('memory_limit')."<br>";flush();
          for($i=0;$i<4;$i++)
          {
          $tstart = microtime(true);
          for($j=0;$j<100000;$j++)
              $testa=str_replace('_aaa','bbb',$testa);
          echo microtime(true)-$tstart.' -memory usage: '.memory_get_usage()."<br>";flush();
          flush();
          }
      ?>

      16432<- memory usage before starting loop with limit at: 8M
      -0.089416 -memory usage: 36480
      0.497955 -memory usage: 36480
      -0.508495 -memory usage: 36480
      0.493202 -memory usage: 36480

      1. Hallo,

        was soll die Zahlenreihe 1 bis 24 darstellen?

        16432<- memory usage before starting loop with limit at: 8M
        -0.089416 -memory usage: 36480
        0.497955 -memory usage: 36480
        -0.508495 -memory usage: 36480
        0.493202 -memory usage: 36480

        wie Du selbst sehen kannst, wird der Arbeitsspeicher innerhalb der Ausführung der Schleifer nicht mehr verändern. Was willst Du jetzt also mit garbage collector memory limit?
        Warum Du negative Ergebnisse bei der Berechnung der Geschwindigkeit erhältst, ist aufgrund Deiner mangelhaften Mitarbeit (z. B. in Form von Versionsangaben PHPs, Angabe zum Webserver, etc.) zur Klärung des Problems spekulativ.

        Jedenfalls ist nicht zu erkennen, wo Du bei 100.000 Durchläufen innerhalb von einer halben Sekunde auf einem meiner Systeme mit der Geschwindigkeit von str_replace() Probleme hast. Du wurdest von mir gebeten, das tatsächlich problematische Script für eine Durchsicht zugängig zu machen, aber auch hier verweigerst Du die Mitarbeit.

        Da kann man wirklich bloß noch "hilfe!" sagen ;)

        Gruß aus Berlin!
        eddi

  3. gudn tach!

    der erste durchgang geht schnell von statten. aber dann dauert es manchmal sogar 10x länger!

    was genau verstehst du unter dem ersten durchgang? (wo faengt der an und wo hoert er auf?)

    hierzu das schlampig geschriebene demonstrationsbeispiel:

    wenn ich das im cli ausfuehre, wird da staendig <br>6<br> und <br>7<br> ausgegeben (mit vielen punkten dazwischen).

    prost
    seth

  4. Hi steffi,

    hab nun lange versucht, herauszufinden, warum eines meiner import-scripte immer langsamer wird. nun scheine ich das time lag gefunden zu haben. es heißt str_replace(array())!
    der erste durchgang geht schnell von statten. aber dann dauert es manchmal sogar 10x länger!

    Weil str_replace() im ersten Durchgang alle "aaa" ersetzt hat, findet es in allen folgenden Durchgängen natürlich keine mehr. Wenn das Suchen intern zeitaufwändiger ist als das Ersetzen, könnte sich so der Zeitunterschied erklären.

    hierzu das schlampig geschriebene demonstrationsbeispiel:

    <?
    set_time_limit(0);
    $ts_=0;
    $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
    $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
    $testa[]='ccccccccccccccccccccccccc';
    $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
    $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
    $testa[]='ccccccccccccccccccccccccc';
    $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
    $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
    $testa[]='ccccccccccccccccccccccccc';
    $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';
    $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';
    $testa[]='ccccccccccccccccccccccccc';
    for($i=0;$i<10000;$i++)
    {
    $ts=time();echo ".";flush();
    for($j=0;$j<1000;$j++)
      $test=str_replace('aaa','bbb',$testa);
    $ts_=$ts_+(time()-$ts);
    $ts=time();
    if(bcmod($i,100)==0)
    {
      echo '<br>'.$ts_.'<br>';
      $ts_=0;
    }
    }
    ?>

    was kann ich tun, damit jeder durchgang so schnell geht wie der erste?

    In dem speziellen Fall: Bei jedem Ersetzvorgang ein Array bereitstellen, in dem so viele 'aaa' vorhanden sind wie $testa vor Beginn der Schleifen. Guckst du mal hier:

    <?php  
      
    set_time_limit(0);  
      
    // nur zum Zeit-Messen ...  
    function calculate_runtime(  
      $time = FALSE // NUMERIC: Ein gültiger Zeitstempel  
    ){  
      list($usec, $sec) = explode(' ', microtime());  
      if(FALSE === $time) return ( (float)$sec + (float)$usec );  // Startzeit zurueckgeben  
      if( !is_numeric($time) ) return( $this->return_error(E_WARNING, 'parameter has to be of NUMERIC type') );  
      return( (float)$sec + (float)$usec - $time );   // uebergebene Zeit von aktueller Zeit abziehen  
    }  
      
      
    $ts_=0;  
    $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';  
    $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';  
    $testa[]='ccccccccccccccccccccccccc';  
    $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';  
    $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';  
    $testa[]='ccccccccccccccccccccccccc';  
    $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';  
    $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';  
    $testa[]='ccccccccccccccccccccccccc';  
    $testa[]='aaaaaaaaaaaaaaaaaaaaaaaaa';  
    $testa[]='bbbbbbbbbbbbbbbbbbbbbbbbb';  
    $testa[]='ccccccccccccccccccccccccc';  
      
    $start = calculate_runtime();  
      
    for($i=0;$i<10000;$i++)  
    {  
     //$ts=time();echo "."; //flush();  
     for($j=0;$j<1000;$j++)  
      $testb = $testa;  
      $test=str_replace('aaa','bbb',$testb);  
      
     /*  
     $ts_=$ts_+(time()-$ts);  
     $ts=time();  
     if(bcmod($i,100)==0)  
     {  
      echo '<br>'.$ts_.'<br>';  
      $ts_=0;  
     }  
     //*/  
    }  
      
    printf('<hr />dauer(%s)', calculate_runtime($start) );  
    ?>
    

    MffG
    EisFuX

    --
    Auch meine Hosenträger sind intelligent, in dem Sinne, dass man sie regulieren kann. Sie besitzen ein adaptives Verhalten.
    Stanisław Lem
    1. Hallo,

      Weil str_replace() im ersten Durchgang alle "aaa" ersetzt hat, findet es in allen folgenden Durchgängen natürlich keine mehr.

      for($i=0;$i<10000;$i++)
      {
      for($j=0;$j<1000;$j++)
        $test=str_replace('aaa','bbb',$testa);
      }

      das ist Unsinn. $testa wird zu keiner Zeit überschrieben. Jedem Schleifendurchlauf liegen die _selben_ Daten zugrunde.

      Gruß aus Berlin!
      eddi

      1. Hallo eddi,

        das ist Unsinn. $testa wird zu keiner Zeit überschrieben. Jedem Schleifendurchlauf liegen die _selben_ Daten zugrunde.

        Schimpf und Schande über mich -- da ist mir wohl beim Hin- und Herkopieren was durcheinander geraten. Klar, str_replace() überschreibt ja nichts, sondern gibt das Ergebnis der Ersetzungsvorgangs zurück.

        MffG
        EisFuX

        --
        Ich verneige mich vor meinen Hosenträgern ...