hotti: Array Element richtig löschen

Moin,

bischen Code:

  
my @a = qw(1 2 3 4 5 6);  
delete $a[1];  
print join("\n", @a),"\n";  

Ergebnis:
1

3
4
5
6

Scheinbar ist $a[1] noch vorhanden, wie bekomme ich das ganz weg?

Bitte mal um Hinweise,
Hotti

  1. http://perldoc.perl.org/functions/delete.html
    http://perldoc.perl.org/functions/splice.html

    1. http://perldoc.perl.org/functions/splice.html

      An splice() hab ich auch schon gedacht, habe jedoch Bedenken, dass bei jedem splice()-Aufruf die gesamte (sehr lange) Liste jedesmal durchgegagngen wird, da würde die Performance in die Knie gehen. Na, ich werde mal einen trace machen...

      Hotti

      --
      Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
      1. Moin Moin!

        Devel::NYTProf

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
        1. Moin Moin!

          Devel::NYTProf

          Teuflisch ;)

          Video <= Klasse!

          Danke,
          Hotti

          --
          # devil
        2. Moin Moin!

          Devel::NYTProf

          wenn Du grade hier bist und bevor ich da ran gehe...

          mir gehts um eine Suche durch ein Liste mit 20T Einträgen, realisiert mit Text::Query. Bei einem Match wird ein Ergebnis-Array geschrieben, was im Extremfall (alles matcht) jedoch heißt: ich habe das Array doppelt im RAM.

          Überlegung: Das zu durchsuchende Array wird zum Ergebnis-Array gemacht, indem diejenigen Elemente gelöscht werden, die keinen Match ergeben. Der Vorteilsgewinn RAM-Ersparnis und weniger CPU setzt natürlich voraus, dass das Löschen nicht benötigter Listeneinträge die Sache nicht ausbremst.

          Hast Du sowas schonmal umgesetzt?

          Hotti

          1. Moin Moin!

            wenn Du grade hier bist und bevor ich da ran gehe...

            mir gehts um eine Suche durch ein Liste mit 20T Einträgen,

            T wie Tera oder T wie Tausend?

            Wenn Du 20 TeraEinträge komplett ins RAM bekommst, und seien es nur Booleans, bin ich ein wenig neidisch auf Deine Hardware.

            Bei 20.000 ist die Frage eigentlich nur, wie groß so ein Eintrag im Durchschnitt ist. Perl hat natürlich etwas Overhead, sowohl bei Skalaren als auch bei Arrays, nicht zuletzt um für concat / shift / unshift / push / pop und ähnliche Operationen nicht sofort neuen Speicher belegen zu müssen. Nehme ich mal 1 kByte Rohdaten pro Eintrag an, und 100% Overhead, dann wären das mal gerade eben 40 MByte. Damit kann Perl selbst auf Embedded Systemen problemlos umgehen, erst recht auf PCs aus diesem Jahrhundert. Und so lange dabei keine massiven Geschwindigkeitsprobleme auftreten, würde ich die Daten auch notfalls doppelt und dreifach im Speicher halten.

            Sinnvolle Obergrenze ist die für den Perl-Prozess zur Verfügung stehende Menge an echtem RAM, ohne Swap. Sobald Du das System zum Swappen zwingst, bricht Dir die Performance massiv ein. An dem Punkt investierst Du in RAM, bis das Swappen aufhört, dann in eine größere Maschine, die noch mehr RAM verträgt. Oder Du investierst entsprechend viel Aufwand, um den Algorithmus weniger speicherhungrig zu machen, ohne gleichzeitig grottenlahm zu werden. Oder Du kaufst Dir entsprechendes Know-How als Fertiglösung ein. Die üblichen Verdächtigen für den schnellen Umgang mit größeren Datenmengen heißen relationale Datenbanken (Oracle, PostgreSQL, eingeschränkt auch SQLite, mit einem zugedrückten Auge auch MS SQL Server, mit zwei zugedrückten Augen auch MySQL und Access).

            realisiert mit Text::Query.

            Das Modul ist bei CPAN auf dem Stand von 1999, also mal entspannte 12 Jahre nicht gewartet worden. Sammelst Du antike Software oder stehst Du einfach nur auf Schmerzen?

            In beiden Fällen hätte ich sehr interesante Jobs für dich, 20 bis 50 Jahre alte, von Amateuren zusammengefrickelte Software, basierend auf Nischen-Software, Fall 1 mit kräftigem Lochkartenduft und netten Überraschungen wie signifikanten Leerzeichen, Fall 2 etwas jünger, dafür von Grund auf kaputt gebastelt schon ab Werk und komplett undokumentiert. Oh, und in beiden Fällen ist natürlich jede Änderung am Code mit einem Stapel Papier zu beantragen, zu genehmigen, zu dokumentieren und zu archivieren. Nicht, dass das in den letzten Jahrzehnten immer passiert wäre ... [1]

            Bei einem Match wird ein Ergebnis-Array geschrieben, was im Extremfall (alles matcht) jedoch heißt: ich habe das Array doppelt im RAM.

            Warum merkst Du Dir nicht einfach nur die Indizes der Matches?

            Ansatz:

            @results=grep { isWanted($data[$_]) } 0..@data-1;

            Rausschreiben entsprechend:

              
            foreach my $result (@results) {  
                say $data[$result];  
            }  
            
            

            Überlegung: Das zu durchsuchende Array wird zum Ergebnis-Array gemacht, indem diejenigen Elemente gelöscht werden, die keinen Match ergeben.

            Ich habe gerade nicht im Kopf, wie Perl Array-Elemente verwaltet, und ich habe gerade keinen Nerv, mich durch die Sources zu wühlen. Ich rate einfach mal, dass da ziemlich stumpf eine Liste von Zeigern auf SVs im Speicher steht. delete($array[$idx]) bzw. $array[$idx]=undef würde dann nur den entsprechenden Zeiger auf SvNULL umbiegen und den Referenz-Counter des SVs um eins verringern. Ist der Referenz-Counter Null, wird der vom SV belegte Speicherplatz wieder in Perls interner Speicherverwaltung freigegeben -- nicht aber im Betriebssystem. Perl gibt nie Speicher an das Betriebssystem zurück (sagt man jedenfalls in gut informierten Kreisen).

            Natürlich braucht das Freigeben von Speicher in der internen Speicherverwaltung Zeit. Ansonsten dürfte das Verbiegen der Zeiger relativ schnell sein, letzten Endes eine Zuweisung von 32 oder 64 Bit. splice() würfelt das Array ziemlich durcheinander, wenn Du in kleinsten Schritten ein Element nach dem Anderen aus dem Array wirfst. Jeder einzelne Splice-Lauf schiebt einen größeren Haufen Zeiger im Speicher des Arrays hin und her, schätze ich. Das geht dank rep movsd auf x86 recht flott, dauert aber natürlich länger als eine einfache Zuweisung.

            Der Vorteilsgewinn RAM-Ersparnis und weniger CPU setzt natürlich voraus, dass das Löschen nicht benötigter Listeneinträge die Sache nicht ausbremst.

            It depends -- deswegen der Hinweis auf Devel::NYTProf.

            Hast Du aktuell ein Problem mit Geschwindigkeit und/oder RAM-Auslastung?

            Nein? Dann gilt: premature optimization is the root of all evil (Donald Knuth). Finger weg, warte bis es brennt.

            Ja? Dann Devel::NYTProf laufen lassen und an den Hot Spots optimieren. Wash, rinse, repeat, bis Du ohne unvernünftig hohen Aufwand nicht mehr weiter kommst.

            Gleichzeitig schneller werden und weniger RAM verbrauchen klappt meistens nicht, ohne den Algorithmus gründlich zu überdenken. Meistens bist Du entweder schnell oder sparsam mit dem RAM, aber nicht beides. Wenn Du es schafft, unnötige Kopien großer Datenmengen zu vermeiden, sparst Du oft gleichzeitig RAM und Zeit. Dazu habe ich Dir oben schon einen Tipp gegeben.

            Hast Du sowas schonmal umgesetzt?

            In dieser Form nicht. Aber ich habe vor einer Weile auf die harte Tour herausfinden müssen, dass ein Primitiv-Parser, der einfach immer nur ein erkanntes Stückchen Text vom Anfang eines Strings abschneidet (per s/^pattern//), mit Strings im Megabyte-Bereich absolut keinen Spaß mehr macht. Läßt man den String dagegen unverändert und merkt sich nur die Position, an der das letze erkannte Stückchen Text endete (m/\Gpattern/g), sind auch mehrere Megabyte große Strings kein Problem.

            Bei der Gelegenheit habe ich auch die lahmen Innereien von XML::Twig durch Devel::NYTProf gejagt und bin dann sehr verzweifelt zu XML::LibXML gewechselt. Seitdem ist XML::LibXML meine Standard-Library für XML. XML::Twig könnte deutlich schneller sein, wenn es nicht ständig bereits bekannte Daten neu berechnen und wie blöd im Speicher hin und her kopieren würde. Der Reparaturauffwand war aber wesentlich höher als der Aufwand für einen Wechsel auf XML::LibXML.

            Alexander

            [1] Nein, ich denke mir das nicht aus. Fall 1 ist mein aktueller Job, Fall 2 ist der Job, für den ich ursprünglich mal eingestellt wurde, und der nun in Panik aufs Abstellgleis geschoben wurde, weil die zwei Fall 1 betreuenden Kollegen in absehbarer Zeit in Rente gehen werden. Wenigstens stimmt das Schmerzensgeld ... ;-)

            --
            Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
            1. Moin,

              Das Modul ist bei CPAN auf dem Stand von 1999, also mal entspannte 12 Jahre nicht gewartet worden. Sammelst Du antike Software oder stehst Du einfach nur auf Schmerzen?

              Das Alter des Moduls ist für mich uninteressant, solange es funktioniert.

              In beiden Fällen hätte ich sehr interesante Jobs für dich, 20 bis 50 Jahre alte, von Amateuren zusammengefrickelte Software, basierend auf Nischen-Software,

              Es ist derzeit leider eine brotlose Kunst, meine alten Scripts nach modernen Gesichtspunkten zu überarbeiten, z.B. so zu schreiben, dass zum Austausch einer Datenanbindung von 'Datei auf DB' lediglich ein bis drei neue Codezeilen erforderlich sind. Dazu habe ich meine DB-Zugriffe in Klassen organisiert, was heißt, dass die für das Script darzustellende Ergebnismenge 1:1 genauso aussieht wie diejenige, die Text::Query liefert.

              DB-Anbindung in Klassen heißt u.a. auch, dass sämtliche SQL-Statements nicht in den Scripts verstreut, sondern in einem einzigen Modul notiert sind. Gestern abend habe ich mich in die Fulltext-Indizierung für MySQL eingelesen, auf gehts zum neuen Modul myBase::Unicode ;)

              Schönen Sonntag,
              Hotti

              1. Moin,

                DB-Anbindung in Klassen heißt u.a. auch, dass sämtliche SQL-Statements nicht in den Scripts verstreut, sondern in einem einzigen Modul notiert sind. Gestern abend habe ich mich in die Fulltext-Indizierung für MySQL eingelesen, auf gehts zum neuen Modul myBase::Unicode ;)

                fertisch

                Schönen Sonntag weiterhin,
                Hotti

                --
                Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
              2. Moin,

                Das Modul ist bei CPAN auf dem Stand von 1999, also mal entspannte 12 Jahre nicht gewartet worden. Sammelst Du antike Software oder stehst Du einfach nur auf Schmerzen?

                Das Alter des Moduls ist für mich uninteressant, solange es funktioniert.

                ... Gestern abend habe ich mich in die Fulltext-Indizierung für MySQL eingelesen, auf gehts zum neuen Modul myBase::Unicode ;)

                MySQL: Match, against, in boolean mode... funktioniert prima und blitzschnell. Jedoch, in diesem speziellen Anwendungsfall geht es um die Suche nach Strings, die oftmals kürzer als vier Zeichen sind, da wäre ein Feintuning in der Serverkonfiguration erforderlich (wo ich nicht rankomme) und die Stopwortliste müsste auch angepasst werden. Es hat also alles seine Vor- und Nachteile, der Vorteil von Text::Query liegt in der Komfortabilität und Kompatibilität und macht den Nachteil, dass die Suche eine Sekunde länger dauert, wieder wett.

                Hotti

      2. http://perldoc.perl.org/functions/splice.html
        An splice() hab ich auch schon gedacht, habe jedoch Bedenken, dass bei jedem splice()-Aufruf die gesamte (sehr lange) Liste jedesmal durchgegagngen wird, da würde die Performance in die Knie gehen. Na, ich werde mal einen trace machen...

        undefe die betreffenden Elemente,
        am Schluss reorganisiere den Array mit grep

        mfg Beat

        --
        ><o(((°>           ><o(((°>
           <°)))o><                     ><o(((°>o
        Der Valigator leibt diese Fische
        1. http://perldoc.perl.org/functions/splice.html
          An splice() hab ich auch schon gedacht, habe jedoch Bedenken, dass bei jedem splice()-Aufruf die gesamte (sehr lange) Liste jedesmal durchgegagngen wird, da würde die Performance in die Knie gehen. Na, ich werde mal einen trace machen...

          undefe die betreffenden Elemente,
          am Schluss reorganisiere den Array mit grep

          Jow, auch ne Variante für diese kleine Anwendung, die ich gerne noch ein bischen optimieren möchte. Oder die Suche über die Datei legen und diese nicht erst auf ein Array einlesen. Oder die Suche über mysql ;)

          Hotti