array algos

  • Ich bin auf der suche nach verschiedenen Möglichkeiten wie man am schnellsten mir array's umgeht.


    Array's initialisieren kann man ja ganz gut mit memset machen.
    Auch das kopieren von arrays geht gut und schnell mit memcpy


    Was ist aber wenn ich das maximum oder den avg in einem Array finden will?
    Gibt es dafür optimierte Möglichkeiten unter cpp?


    Oder wenn ich überprüfen will ob alle Werte == 0 sind.
    Da ich recht grosse arrays habe, wäre ich für diesen Fall für einen Tip dankbar.


    Eine "for" Schleife über alle Elemente ist zwar einfach, aber auch recht langsam....


    hmmm mir ist zwar gerade was eingefallen aber ich hoffe das jemand von euch vielleicht einen Tip hat.


    Vielleicht kennt ja jemand auch eine gute Seite wo solche Tips besprochen werden.

  • Wenn es sich einfach um ein unsortiertes Array handelt, bleibt dir wohl nichts anderes übrig, als eine Schleife zu benutzen, schließlich muss jedes Feld in die Überlegung einbezogen werden, ob ein bestimmter Wert Maximum oder ob alle Felder = 0 sind. Beim Durchschnitt kannst du natürlich ein paar repräsentative Werte nehmen, falls der nicht so genau sein muss.


    Weitere Optimierungen kannst du aber nur dann vornehmen, wenn du ein bischen was über die Daten weißt oder sie sortierst. Entscheidend ist, woher die Daten in welcher Form kommen und wie oft du welche Operationen darauf ausführen musst.


    Bei der Situation "zufälliges Array ohne weitere Informationen" kann man meines Wissens das Maximum und ob alle = 0 sind nur mit der Ordnung O(n) ermitteln. Zu deutsch: Bei n Elementen braucht man n Operationen (also Vergleiche). Wenns besser werden soll, musst du ein paar Infos mehr ausspucken...

  • OK fangen wir mal mit einer ganz schlechten Version an:

    Code
    int my_array[]={10,3,35,101,65,100};
    int elements = sizeof(my_array)/sizeof(int);
    int max=0;
    for (int i=0;i<elements:i++){
      if (max<my_array[i])
         max=my_array[i];
    }
     cout << "max: " << max << endl;


    Besser ist:

    Code
    int my_array[]={10,3,35,101,65,100};
     int elements = sizeof(my_array)/sizeof(int);
     sort(my_array, my_array+elements);
     cout << "max: " << my_array[elements-1] << endl;

    Problem hierbei ist nur das das array verändert wird.


    Sinn der ganzen Sachen ist:
    Ich möchte in einem Bild (als Array) alle Pixel auf 0 setzen wenn sie kleiner als (max - x) sind.


    An einer anderen Stelle möchte ich überprüfen ob alle (pixel <= x) sind.
    (x kann z.B. 3 sein.)

  • Die Idee mit dem Sortieren ist definitiv nicht besser. Selbst der beste Sortieralgorithmus hat eine Ordnung von O(n*log(n)). (Da bringt mir Informatik als Nebenfach zum Physik-Studium ja auch mal was). Soll heißen: Allein das Sortieren dauert länger, als das Array einfach abzulaufen. Sortieren hilft hier nur, wenn zwischen zwei Test einzelne Werte hinzukommen und diese direkt sortiert eingefügt werden könnten.


    Für dein Problem habe ich aber jetzt noch keine Idee, um das zu optimieren...

  • Um alles auf "0" zu überprüfen, würde mir noch ein dirtyTrick einfallen.
    Weiß nur gerade nicht ob der geht.


    Also mein array ist kein "int" array sondern ein "uint_8t" array (also 8 bit)
    Wenn ich nun aber immer 16(oder vielleicht 32) bit mit 0 vergleichen würde, müsste ich nur jeden zweiten(oder vierten) vergleichen.


    Soweit zur Theorie.
    Ob das was bringt weiß ich nicht.
    Müßte dann ja immer casten.
    Werde ich mir mal überlegen.


    Gibt es denn nicht von cpp irgendeine optimierte methode?

  • Hi, wenns wirklich schnell gehen soll, dann empfehle ich MMX:


    http://www.fh-zwickau.de/doc/prmo/mmxtutor/text/


    Man kann mit dem "PCMPGTB" -Befehl gleich 8 Bit vergleichen:
    http://www.fh-zwickau.de/doc/prmo/mmxtutor/text/mmx_10.htm


    Wenn man das Ergebis von obeigen Befehl mit "PSUBUSB" (Subtraktion mit signed saturation) vom Original abzieht, sind alle Bytes 0. Somit hätte man mit 2 Befehlen 8 Bytes gleichzeitig bearbeitet (overhead für MMX-Initialisierung nicht mit gerechnet)


    z.B. so (code nicht getestet)
    MM7 = 40 40 40 40 40 40 40 40 // Schwellwert
    [speicher] = 20 30 40 50 60 70 80 90 // Pixel


    :loop
    MOVQ [speicher], MM1 // 8 bit aus Speicher in MM0 laden


    MOVQ MM7, MM0 // Schwellwert in MM0 kopieren


    PCMPGTB MM0, MM1 // Ergebnis: MM0=FF FF 00 00 00 00 00 00


    PSUBUSB MM1, MM0 // Ergebis: MM1= 00 00 40 50 60 70 80 90


    MOVQ MM1,[speicher] // abspeichern


    // speicher++8
    jump loop


    Durch ein weig loop unrolling lässt siich dann noch einiges heraus holen, wenn man die Zugriffe auf die einzelenen Prozessorteile gleichmäßig verteilt.


    Der Nachteil von MMX ist, dass das Array eine vielfache Größe von 8 haben muss/soll, sonst wirds tricky zum programmieren.


    Gruß
    Roland

    Software: VDR 1.4.3, mp3, osdpip, streamdev-server, femon, wapd, X11, Wireless Keyboard Kernel: 2.6.18
    Hardware: 1x DVB-S v 1.3, 1x Skystar 2, Celeron@2GHz, 256 MB RAM, 4 HDs Raid1/5, Total: 600 GB, Asus P4S533 cmi8738 & LAN on board 6 PCI
    40" Sammelbestellungs-LCD an ATI Radeon 9550 DVI-Out + tvtime, 70 cm TV an J2-RGB-Out
    Organisator der ersten und zweiten VDR-Sanitizer Sammelbestellung.
    In progress: POV-ION 330 - MediaPointer MP-S2 - vdr 1.7.9 - vdr-xine(vdpau)

  • Das ist ganz gut.
    Auf der Seite ist auch ein Beispiel für Filterung.
    Ich wende auch einen Soble Filter an.
    Das kann ich dann auch mal optimieren.


    Das doofe dabei ist nur das man die Algos doppelt schreiben muss.
    Einmal für MMX und einmal ohne.


    Was passiert wenn ich einen MMX Befehl ausführe ohne zu testen ob die CPU das kann?
    MMX ist ja eigentlich Intel Sache.


    Mein Arbeitsrechner ist aber ein AMD

  • ...dann stürzt das Programm ab mit illegal execution.


    MMX können eigentlich alle neueren Prozessoren ab 500 MHz
    (bei den VIA/EPIA bin ich mir da im Moment nicht so sicher)


    Einfach mal "cat /proc/cpuinfo" machen, da steht dann was der Prozi alles kann.


    Gruß
    Roland

    Software: VDR 1.4.3, mp3, osdpip, streamdev-server, femon, wapd, X11, Wireless Keyboard Kernel: 2.6.18
    Hardware: 1x DVB-S v 1.3, 1x Skystar 2, Celeron@2GHz, 256 MB RAM, 4 HDs Raid1/5, Total: 600 GB, Asus P4S533 cmi8738 & LAN on board 6 PCI
    40" Sammelbestellungs-LCD an ATI Radeon 9550 DVI-Out + tvtime, 70 cm TV an J2-RGB-Out
    Organisator der ersten und zweiten VDR-Sanitizer Sammelbestellung.
    In progress: POV-ION 330 - MediaPointer MP-S2 - vdr 1.7.9 - vdr-xine(vdpau)

  • Ich würde einfache Schleifen verwenden, und den Rest dem Compiler überlassen, der ist ja beim Optimieren auch nicht ganz dumm. Selbst wenn der Code durch Zusammenfassen von 4-8 Bytes zu dword bzw. mmx-Registern schneller wird, in der Praxis ist die Gesamtlaufzeit entweder so minimal, dass es keinen Unterschied macht, oder das Array ist schon so groß, dass eh die Geschwindigkeit des Arbeitsspeichers das entscheidende ist.


    Nimm als Beispiel ein 1Mb-Array, und verschwende pro Byte 10 CPU-Takte (das sind 20-30 Instruktionen!), dann hast du bei 1GHz trotzdem alles in 0.01s erledigt.


    Wenn es unbedingt mmx sein muss, empfehle ich die Verwendung von intrinsics-Kapselungen wie zb. __builtin_ia32_pcmpgtb() statt direktem Assemblercode. Macht den Code lesbarer, man kann mit Variablen statt Registern arbeiten, und der Optimierer kümmert sich um die optimale Anordnung der Befehle.


    Gruß,


    Udo

  • Hallo,


    für einige generische Algorithmen unter cpp kann ich einen Blick in die STL empfehlen. Eine brauchbare Referenz findet sich z.B. unter http://www.sgi.com/tech/stl/


    Die STL-Algorithmen werden in Hinblick auf Generalität und Effizienz implementiert und dürften ausser in echten Spezialfällen die optimale Wahl sein.



    Cya, Ed

  • Ich habe hier ein array mit (max 1920x1080=2073600) Feldern.
    Und das ca 25x60x60x~2=180000 mal.
    EXTREMFALL
    Und dann muss ich auch noch öfter über jedes Array rüber.


    Da macht es schon gut was aus was man macht.
    Auf der MMX Seite wird der Vergleich zu 1:2,5 gesagt.
    Das wäre schon ein ganz guter unterschied.
    Ich muß mal sehen ob ich das mit meinen Kenntnissen hinbekomme.


    Hier mal der Vergleich(auch von der Seite)
    Dabei wird ein Bild um einen Wert aufgehellt.








    In diesem Einfachher Beispiel ist der Faktor sogar fast 1:8

  • Hi,


    nur mal interessehalber, wie gehen den die Laufzeiten runter,
    wenn Du Version 3 wie folgt abänderst ?


    speziell preincrement (++i), sollte gegenüber postincrement(i++) noch ein paar OP einsparen...



    Andreas

  • Bei den Vergleichsmessungen stört mich schon mal, dass mit keinem Wort erwähnt wird, welcher Compiler eingesetzt wurde, und welche Optimierungen aktiviert waren. Seriös vergleichen kann man so jedenfalls nicht. Ein guter optimierender Compiler sollte die C-Versionen 1 und 2 so gut übersetzen können wie ASM-Version 1, und C-Version 3 so gut wie ASM-Version 2. (sehr gute Optimierer könnten die ASM-Versionen vermutlich sogar schlagen.)


    Dass bereits von C zu ASM Geschwindigkeitsunterschiede von Faktor 2 oder 4 auftreten, spricht jedenfalls nicht für den Compiler. Die Rechnerangabe Pentium MMX 166 spricht auch Bände - das dürfte keinerlei Rückschlüsse auf aktuelle Recher erlauben.


    > speziell preincrement (++i), sollte gegenüber postincrement(i++) noch ein paar OP einsparen...


    Vielleicht in Steinzeit-Compilern. Optimierende Compiler machen das Inkrement dann, wenn es am besten passt. Und das bedeutet, wenn eine Recheneinheit frei ist. Das Herausziehen der Multiplikation aus der Schleife hilft dagegen tatsächlich.


    Als kleinen Test hab ich die Variante von Hulk mal durch GCC 3.3.3 gejagt, mit den flags -march=i686 -masm=intel -funroll-loops -O3 -S. Hier das (etwas aufgeräumte) Ergebnis:

    Wie man sieht, macht die Hauptschleife jetzt jedes mal 16 Bytes in 7 Befehlen, das sollte ASM2 (4 Bytes in 4 Befehlen) deutlich schlagen.


    Gruß,


    Udo

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!