MMX Problem



  • Hallo,

    habe ein wenig gerechnet: 25 Bilder pro Sekunde ergibt 40 ms pro Bild.
    Angenommen, diese gute Funktion muss einmal pro Bild aufgerufen werden und die Funktion führt 1e6 Berechnungen durch - ergibt 40 ns pro Berechnung. Die Funktion muss innerhalb von 40 ms fertig werden, ich glaube, es geht nicht...


  • Mod

    abc.w schrieb:

    Hallo,

    habe ein wenig gerechnet: 25 Bilder pro Sekunde ergibt 40 ms pro Bild.
    Angenommen, diese gute Funktion muss einmal pro Bild aufgerufen werden und die Funktion führt 1e6 Berechnungen durch - ergibt 40 ns pro Berechnung. Die Funktion muss innerhalb von 40 ms fertig werden, ich glaube, es geht nicht...

    Nicht zwingend. Erstens entsprechen 40ns erst einmal nur 25MHz - dann hättest du immer noch 8 Takte Zeit bei einem 200er-Pentium je Element, was ggf. ausreicht. Allerdings gibt es eine weitaus bessere Möglichkeit als die Verwendung von MMX. Da du nur 65536 mögliche Ausgangswerte hast, könntest du genauso gut eine LUT verwenden - da sich B und C in der Regel wohl nicht ändern werden, brauchst du sie nicht jedesmal neu berechnen. Das wiederum erlaubt dir den Luxus einen ggf. etwas langsameren, aber dafür genaueren Algorithmus zur Berechnung zu verwenden. Zudem wirst du bei einer Reduzierung auf 8bit Ausgangssignal wahrscheinlich nicht einmal alle 16bit für das Lookup benötigen, bei einer Videoausgabe wird man minimale Ungenauigkeiten wohl ignorieren können, benutzt du z.B. nur die höherwertigen 12bit des Ausgangssignals, braucht die LUT nur 4KB - selbst ein armer Uralt Pentium mit 8KB L1-Cache sollte hiermit keine Schwierigkeiten haben.

    Plötzlich ist das dann kein Assemblerproblem mehr... deinen Compiler solltest du allerdings durch eine neuere Version ersetzen - mit 6.0 ist der Fun- und Performancefaktor einfach tief im Keller.

    Welche Hardware ist denn zur Berechnung gegeben?



  • Hallo,
    bei dieser Applikation (Nachführen von Teleskopen auf Sterne mit einer Astrokamera) sind keine 25fps notwendig, die Bildrate liegt eher im Bereich von 1fps, aber es wäre schön, wenn der Benutzer dieser Software die Berechnung nicht merkt (max 0.1sec), jedoch verwenden viele Leute langsame Rechner, die auch mal kaputt gehen dürfen. Ich selber verwende einen 500MHz PC für meine Astroaufnahmen.

    Was ist eigentlich eine LUT?
    Welchen Compiler sollte man verwenden?

    Lg,
    Matthias

    Ps. Am Wochenede habe ich evtl. kein Internet.


  • Mod

    LUT = look-up table

    enum { table_bits = 12, shift_bits = sizeof( unsigned short ) * CHAR_BIT - table_bits; }; // 12 signifikante Stelle resultiert in
    typedef unsigned char lut_t[ 1 << table_bits ]; // 4KB look-up table
    
    void GenerateLUT(lut_t* table, int B, float C)
    // keine Umstände, Geschwindigkeit ist hier ohnehin sekundär
    // float hat 23 signifikante Stellen, was bei 16 bit Ausgangswerten genügt
    // so müssen wir uns auch nicht um Überläufe bei Zwischenergebnissen sorgen
    {
        for ( unsigned i = 0; i < ( 1 << table_bits ); ++i )
        {
            float v = ( ( i << shift_bits ) - B ) * C;
            (*table)[ i ] = v <= UCHAR_MAX ? v < 0 ? 0 : (unsigned char)v : UCHAR_MAX;
        }
    }
    
    void AdjustContrast(unsigned char *dest, const unsigned short *src, unsigned long len, lut_t* table)
    {
        for ( unsigned long i = 0; i < len; ++i )
            dest[ i ] = (*table)[ src[ i >> shift_bits ] ];
    }
    

    Irgendein neuerer Compiler ist zu empfehlen.
    z.B. Visual C++ Express 9.0 (VS2008)
    oder etwa mingw-gcc (am besten ein 4er build, z.B. gcc-4.2.1)



  • Genial! - Das ist super, damit kann man ja auch generell das Histogramm verändert (e.g. Logaritmisch,exponential,...) und die Table erstellt man einmal und verwendet anschließend bei jedem Bild. Das muß ich heute Abed umbedingt ausprobieren.

    Kann man auch 2^16 Werte verwenden, oder gibt es einen Grund, warum man die Menge auf 4KB statt 64KB reduzieren sollte?

    Danke,
    Matthias


  • Mod

    mgarza schrieb:

    Kann man auch 2^16 Werte verwenden, oder gibt es einen Grund, warum man die Menge auf 4KB statt 64KB reduzieren sollte?

    Selbstverständlich kann man das tun, einfach durch Änderung der einen Konstante. LUTs sollten möglichst klein sein, um in den Prozessor-Cache zu passen (da die Zugriffsreihenfolge keinem bestimmten Muster folgt, kann man nicht sinnvoll prefetchen). Zwingend erforderlich ist es nicht und letztlich eine Frage der Hardwarevoraussetzungen.



  • Ich habe grade eine Weile gebraucht, bis ich in der ganzen Schieberei durchblicke.
    Funktion GenerateLUT - ok, ist zum Debuggen für Unsterbliche.
    Wie ist das mit der Schleife in AdjustContrast(), kann das sein, dass da ein kleiner Tippfehler eingeschlichen ist, nämlich, statt:

    dest[ i ] = (*table)[ src[ i >> shift_bits ] ];
    

    das hier gemeint ist?

    dest[ i ] = (*table)[ src[ i ] >> shift_bits ];
    

  • Mod

    Richtig, Tippfehler. Wäre beim Debuggen wahrscheinlich schnell aufgefallen durch segfault. Man kann die Schieberei natürlich auch durch Multiplikation und Division darstellen, was evtl. lesbarer ist. Ich schreibe für gewöhnlich kein C, das scheint ein bisschen durch.

    enum
    {
        table_bits = 12,
        src_values = USHORT_MAX + 1,
        scale = src_values >> table_bits,
        table_elems = src_values / scale
    };
    typedef unsigned char lut_t[ table_elems ];
    
    void GenerateLUT(lut_t* table, int B, float C)
    {
        for ( unsigned i = 0; i < src_values; i += scale )
        {
            float v = ( i - B ) * C;
            (*table)[ i ] = v <= UCHAR_MAX ? v < 0 ? 0 : (unsigned char)v : UCHAR_MAX;
        }
    }
    
    void AdjustContrast(unsigned char *dest, const unsigned short *src, unsigned long len, const lut_t* table)
    {
        for ( unsigned long i = 0; i < len; ++i )
            dest[ i ] = (*table)[ src[ i ] / scale ] ];
    }
    

    In C++ bietet sich natürlich ein Funktor an.



  • Hallo,
    ich hab jetzt endlich die Routine implementiert. Und alles läuft super. 🙂

    VIELEN DANK für die Hilfe,
    Matthias



  • mgarza schrieb:

    Hallo Camper,
    ich habe gerade den MMX code ausprobiert, bekomme aber immer einige Fehlermeldungen, daß mm0 und mm1 reservierte Wörter sind:
    Es betrifft folgende Zeilen:
    pinsrw mm0, eax, 0
    pshufw mm0, mm0, 0
    pinsrw mm1, eax, 0
    pshufw mm1, mm1, 0
    Ich hab bislang noch keine Lösung dafür gefunden, muß aber auch zugeben, das meine ASM Kenntnisse sehr bescheiden sind.

    Das Problem, dass "pinsrw" und "pshufw" von VC6 nicht übersetzt werden, liegt daran, dass diese Befehle erst mit der SSE-Spezifikation eingeführt wurden. In der Version 6 wird standardmäßig aber nur maximal MMX unterstützt. Um die Anweisungen kompilieren zu können, mußt Du das "Visual C++ 6.0 Processor Pack" von MS einspielen. Aber Vorsicht: Der Code läuft dann auch nur auf Prozessoren, die SSE unterstützen, ansonsten gibt's 'ne Exception. 😉

    tl123


Anmelden zum Antworten