Ideen zur Beschleunigung des Codes (Camera Bild einlesen)



  • Ich habe folgenden Code um ein Bild einer Kamera aus dem Speicher auszulesen als r+g+b Daten zu interpretieren und wieder in einer Matrix mit rgb Werten zu speichern.
    Nachteilig ist, dass der Code für ein 5 MP Bild im release Modus ~1000 ms braucht. Also pro Pixel 0.2 µs. Ich habe bislang wenig Erfahrung mit Code Optimierung und typischen Ablaufzeiten. Daher wüsste ich gerne ob Ihr noch Potential für Optimierungen in folgendem Code findet:

    (gemessen wird die Zeit von 'convertMemoryToRGBMatrix();')

    void QuEyeCamera::acquireImage()
    {
        if ( m_hCam == 0 )
            openCamera();
    
        if ( m_hCam != 0 )
        {
            // FreezeVideo takes picture and save it to
            // assigned memory
            if( is_FreezeVideo( m_hCam, IS_WAIT ) == IS_SUCCESS ) {
                QTime time;
                time.start();
                convertMemoryToRGBMatrix();
                qDebug() << "Time (convertMemoryToRGBMatrix): " << time.elapsed();
            } else {
                qDebug() << " Get Single Image Failed";
            }
        }
    }
    
    void QuEyeCamera::convertMemoryToRGBMatrix()
    {
        // continue when we have a valid image buffer
        if( m_pcImageMemory != NULL )
        {
            // get pointer to active image buffer
            void *pMemVoid;
            is_GetImageMem( m_hCam, &pMemVoid );
    
            // pitch gibt das Zeileninkrement in Bytes zurück.
            // Das Zeileninkrement ist die Anzahl Bytes vom Beginn einer Zeile
            // bis zum Beginn der nächsten Zeile.
            int nPitch;
            is_GetImageMemPitch( m_hCam, &nPitch );
    
            // calculate pixel offset (pixel locatation in memory)
            char* pPixelPointer;
    
            d->rgbMatrix.setSize(m_nSizeX, m_nSizeY);
            int r,g,b; r = g = b = 0;
            m_nColorMode = colorMode();
            for ( int x = 0; x < m_nSizeX; x++ ) {
                for ( int y = 0; y < m_nSizeY; y++ ) {
                    // (x*bytes) + (y * sizex)
                    long lMemoryPixelOffset = ((long)x * m_nBytesPerPixel) + ((long)y * nPitch );
                    pPixelPointer = (reinterpret_cast<char*> (pMemVoid) + (lMemoryPixelOffset)) ;
    
                    pixelToRGB(pPixelPointer, r, g, b);
                    d->rgbMatrix.setPixel(x,y,r,g,b);
                }
            }
        }
    }
    
    void QuEyeCamera::pixelToRGB(char* pPixelPointer, int & r, int & g, int & b)
    {
        // some variables for pixel peek
        // 32 bit
        unsigned int  nPixVal  = 0;
    
        // pixel peek according color format
        switch( m_nColorMode )
        {
        case IS_SET_CM_RGB32:
            // = IS_CM_RGBA8_PACKED
            // <r(8) g(8) b(8)> <r(8) | g(8) b(8)> <r(8) g(8) ... >
    
        case IS_SET_CM_RGB24:
            // = IS_CM_RGB8_PACKED
            // <r(8) g(8) b(8)> <r(8) | g(8) b(8)> <r(8) g(8) ... >
    
            r = static_cast<int>(*(reinterpret_cast<char*>(pPixelPointer+2)));
            g = static_cast<int>(*(reinterpret_cast<char*>(pPixelPointer+1)));
            b = static_cast<int>(*(reinterpret_cast<char*>(pPixelPointer+0)));
            break;
    ...
        default:
            // we never should pass this point
            return;
        }
    }
    
    void QRgbMatrix::setPixel(int x, int y, int r, int g, int b)
    {
        int pos = ArrPos(x,y);
        d->red[pos] = r;
        d->green[pos] = g;
        d->blue[pos] = b;
    }
    
    int ArrPos(int x, int y) const
    {
        // y + height * x
        return (x + m_size_x * y);
    }
    

    Interessant wäre vor allem ob es einen Teil gibt der besonders aufwendig ist.



  • Im Releasemodus und mit allem Optimierungen kompilieren.
    Zeilenweise durchlaufen statt spaltenweise.
    Bin mir nicht sicher, ob Arrpos und setPixel inline-expandiert werden; vermutlich aber nicht. Deshalb wird der millionenfache Aufruf dieser beiden Funktionen die meiste Zeit kosten.
    Auch muss nicht für jedes Pixel neu ausgerechnet werden wo es im Speicher steht, das geht auch mit nur einer Addition pro Pixel.
    Und das switch in pixelToRGB, für jedes Pixel, könnte man auch nur einmal machen.

    Noch eine Kleinigkeit:

    void QuEyeCamera::pixelToRGB(const char* pPixelPointer, int  & r, int & g, int & b)
    //                           ^^^^^ !!!
    

    Wenn du das alles umstellst ist dein Code mindestens 20x so schnell.



  • Nukularfüsiker schrieb:

    Im Releasemodus und mit allem Optimierungen kompilieren.
    Zeilenweise durchlaufen statt spaltenweise.

    du meinst erst y, dann x?

    Nukularfüsiker schrieb:

    Bin mir nicht sicher, ob Arrpos und setPixel inline-expandiert werden; vermutlich aber nicht. Deshalb wird der millionenfache Aufruf dieser beiden Funktionen die meiste Zeit kosten.

    ArrPos habe ich absichtlich in der header Datei der Klasse, damit es inlined wird. Oder sollte ich damit falsch liegen? setPixel dagegen kann vermutlich nicht inlined werden.

    Nukularfüsiker schrieb:

    Auch muss nicht für jedes Pixel neu ausgerechnet werden wo es im Speicher steht, das geht auch mit nur einer Addition pro Pixel.

    was meinst du genau?

    Nukularfüsiker schrieb:

    Und das switch in pixelToRGB, für jedes Pixel, könnte man auch nur einmal machen.

    Du meinst die Funktionen convertMemoryToRGBMatrix() und pixelToRGB zusammenzusfassen und die For-Schleifen in jeden switch reinschreiben?
    Dass gibt dann allerdings keine schönen Code.

    Noch eine Kleinigkeit:

    void QuEyeCamera::pixelToRGB(const char* pPixelPointer, int  & r, int & g, int & b)
    //                           ^^^^^ !!!
    

    Das funktionert nicht ganz. Zumindest scheitern danach die Zeilen mit 'reinterpret_cast', also z.B.:

    nPixVal = *(reinterpret_cast<unsigned int*>(pPixelPointer));
    

    weil nicht von const konvertiert werden kann:

    reinterpret_cast': 'const char *' kann nicht in 'unsigned int *' konvertiert werden

    Das kann ich natürlich weg-casten, aber ist das empfehlenswert das so zu machen?

    unsigned char cPixVal1 = *(reinterpret_cast<const char*>(pPixelPointer));
    

    (dieser Code Ausschnitt ist für einen andere Farbdefinition, hier SW, die ich vorher nicht im Code gepostet hatte)



  • pospiech schrieb:

    du meinst erst y, dann x?

    Ja, das ist die einfachste Optimierung und sollte schon ein bisschen was bringen.

    ArrPos habe ich absichtlich in der header Datei der Klasse, damit es inlined wird. Oder sollte ich damit falsch liegen? setPixel dagegen kann vermutlich nicht inlined werden.

    Bei ArrPos klappts vielleicht doch. setPixel inlinen würde jedenfalls eine Menge bringen.

    Nukularfüsiker schrieb:

    Auch muss nicht für jedes Pixel neu ausgerechnet werden wo es im Speicher steht, das geht auch mit nur einer Addition pro Pixel.

    was meinst du genau?

    y * pitch muss nur einmal pro Zeile ausgerechnet werden. Wenn du dann die Zeile abläufst, reicht quasi pixelPos = pixelPos + 1. Spart eine Multiplikation pro Pixel.

    Du meinst die Funktionen convertMemoryToRGBMatrix() und pixelToRGB zusammenzusfassen und die For-Schleifen in jeden switch reinschreiben?
    Dass gibt dann allerdings keine schönen Code.

    Da musst du dich vielleicht entscheiden, ob du schönen oder schnellen Code willst. Vielleicht kriegst du es auch so umgestellt, dass der Compiler es inlinen kann. Wie auch immer.

    Das funktionert nicht ganz. Zumindest scheitern danach die Zeilen mit 'reinterpret_cast', also z.B.:

    nPixVal = *(reinterpret_cast<unsigned int*>(pPixelPointer));
    

    weil nicht von const konvertiert werden kann:

    reinterpret_cast': 'const char *' kann nicht in 'unsigned int *' konvertiert werden

    oder kann ich das auch noch loswerden?

    Ginge da nicht einfach

    int r = *(pPixelPointer + 2);
    int g = *(pPixelPointer + 1);
    ...
    


  • Hi,

    habe bereits seit ein paar Jahren die uEye-Kameras unter Qt (Linux+Win32) laufen.

    Du kannst dir das aufwendige Konvertieren sparen, indem du das Farbformat richtig wählst und ein QImage direkt aus den Rohdaten erstellst:

    1. Schritt: Farbformat der Kamera setzen und für später merken

    is_SetImageSize(m_hCamera, m_iImageWidth, m_iImageHeight);
        is_SetColorMode(m_hCamera, nColorMode);
    

    2. Schritt: Speicher (RingBuffer) für die Kamera einrichten

    m_pFrameBuffer = new UEYE_IMAGE[m_iBufferSize];
    
        UEYE_IMAGE *pFrame = m_pFrameBuffer;
        m_pLastFrame       = NULL;
    
        ret = is_ClearSequence(m_hCamera);
    
        for (int i = 0; i < m_iBufferSize; ++i, ++pFrame) {
            if (is_AllocImageMem(m_hCamera, m_iImageWidth, m_iImageHeight, m_iPixelDepth, &pFrame->pBuf, &pFrame->nImageID) != IS_SUCCESS)
                return false;
            if (is_AddToSequence(m_hCamera, pFrame->pBuf, pFrame->nImageID) != IS_SUCCESS)
                return false;
    
            pFrame->nImageSeqNum = i + 1;
            pFrame->nBufferSize = m_iImageWidth * m_iImageHeight * m_iPixelDepth / 8;
        }
    

    3. Schritt: auf Bild warten (is_FreezeImage()) und QImage erstellen

    INT   dummy;
        char *pLast = NULL, *pMem = NULL;
        // wait for new image
        is_FreezeVideo(m_hCamera, IS_WAIT);
        // get current frame
        is_GetActSeqBuf(m_hCamera, &dummy, &pMem, &pLast );
        // set current frame
        for (int i = 0; i < m_iBufferSize; ++i) {
            if (m_pFrameBuffer[i].pBuf == pLast) {
                m_pLastFrame = &m_pFrameBuffer[i];
                break;
            }
        }
    
        // create QImage directly from uEye buffer    
        QImage image((uchar*)m_pLastFrame->pBuf, m_iImageWidth, m_iImageHeight, m_iBytesPerLine, m_ImageFormat);
        image.setColorTable( m_RgbTable );
    }
    

    Das wars! Hiermit funktioniert das Grabben von Bildern binnen weniger Millisekunden (auch im Debug-Modus).

    Hoffe, ich konnte helfen 🙂

    ciao,
    Chris



  • @Nukularfüsiker: Mit deinen Anmerkungen ist der Code insgesamt um einen Faktor 2 schneller geworden. Wo genau die Zeit eingespart wurde habe ich aber nicht nicht versucht zu untersuchen.

    Ich hätte noch ein paar Rückfragen:

    3DH schrieb:

    Hi,

    habe bereits seit ein paar Jahren die uEye-Kameras unter Qt (Linux+Win32) laufen.

    Du kannst dir das aufwendige Konvertieren sparen, indem du das Farbformat richtig wählst und ein QImage direkt aus den Rohdaten erstellst:

    kannst du mir auch sagen welche Farbformate dabei geeignet sind?

    1. Schritt: Farbformat der Kamera setzen und für später merken

    3DH schrieb:

    is_SetImageSize(m_hCamera, m_iImageWidth, m_iImageHeight);
        is_SetColorMode(m_hCamera, nColorMode);
    

    das mache ich sowieso schon.

    3DH schrieb:

    2. Schritt: Speicher (RingBuffer) für die Kamera einrichten

    m_pFrameBuffer = new UEYE_IMAGE[m_iBufferSize];
    
        UEYE_IMAGE *pFrame = m_pFrameBuffer;
        m_pLastFrame       = NULL;
    
        ret = is_ClearSequence(m_hCamera);
    
        for (int i = 0; i < m_iBufferSize; ++i, ++pFrame) {
            if (is_AllocImageMem(m_hCamera, m_iImageWidth, m_iImageHeight, m_iPixelDepth, &pFrame->pBuf, &pFrame->nImageID) != IS_SUCCESS)
                return false;
            if (is_AddToSequence(m_hCamera, pFrame->pBuf, pFrame->nImageID) != IS_SUCCESS)
                return false;
    
            pFrame->nImageSeqNum = i + 1;
            pFrame->nBufferSize = m_iImageWidth * m_iImageHeight * m_iPixelDepth / 8;
        }
    

    Warum ein RingBuffer? Ich habe bislang ohne gearbeitet. Den Buffer des Bildes kann ich ja auch mit einem einzelnen Bild auslesen.

    3DH schrieb:

    3. Schritt: auf Bild warten (is_FreezeImage()) und QImage erstellen

    INT   dummy;
        char *pLast = NULL, *pMem = NULL;
        // wait for new image
        is_FreezeVideo(m_hCamera, IS_WAIT);
        // get current frame
        is_GetActSeqBuf(m_hCamera, &dummy, &pMem, &pLast );
        // set current frame
        for (int i = 0; i < m_iBufferSize; ++i) {
            if (m_pFrameBuffer[i].pBuf == pLast) {
                m_pLastFrame = &m_pFrameBuffer[i];
                break;
            }
        }
    
        // create QImage directly from uEye buffer    
        QImage image((uchar*)m_pLastFrame->pBuf, m_iImageWidth, m_iImageHeight, m_iBytesPerLine, m_ImageFormat);
        image.setColorTable( m_RgbTable );
    }
    

    Wofür brauchst du das 'is_GetActSeqBuf(m_hCamera, &dummy, &pMem, &pLast );'
    Bei mir lese ich die Daten aus dem Buffer 'd->ImageMemory' den ich so zuweise

    int result =  is_AllocImageMem(	d->handleCamera,
                                    d->SizeX,
                                    d->SizeY,
                                    d->BitsPerPixel,
                                    &d->ImageMemory,
                                    &d->MemoryId);
        if( result == IS_SUCCESS )
        {
            is_SetImageMem (d->handleCamera, d->ImageMemory, d->MemoryId);	// set memory active
    

    Um das QImage mit deiner Methode zusammenzustellen fehlt mir noch eine Vorschrift zur Berechnung von 'm_iBytesPerLine' und ich weiß nicht wie sich 'm_RgbTable' zusammensetzt. Kannst du mir dazu weitere Informationen zukommen lassen?



  • Die Berechnung der aktuellen Pixelposition ist wg. multiplikation nicht besonders effizient:

    for ( int x = 0; x < m_nSizeX; x++ ) {
                for ( int y = 0; y < m_nSizeY; y++ ) {
                    // (x*bytes) + (y * sizex)
                    long lMemoryPixelOffset = ((long)x * m_nBytesPerPixel) + ((long)y * nPitch );
                    pPixelPointer = (reinterpret_cast<char*> (pMemVoid) + (lMemoryPixelOffset)) ;
    
                    pixelToRGB(pPixelPointer, r, g, b);
                    d->rgbMatrix.setPixel(x,y,r,g,b);
                }
            }
    

    Für jedes Pixel werden zwei Multiplikationen und zwei Additionen ausgeführt (plus die Inkremente der Schleifen). Besser ist es, wenn Du eine Datenstruktur bereit hälst, die Dein aktuelles Pixelformat abbildet:

    // Diese struct muss der organisation eines Pixels im Speicher entsprechen
    struct RGBPixel
    {
        unsigned char r;
        unsigned char g;
        unsigned char b;
        unsigned char x; // unused, 32bit alignment
    };
    
    // jetzt der Zugriff
    RGBPixel* imageIterator = getImageStart(); // Hier den Start des Bildes im Speicher bestimmen
    for( int y = 0; y < rows; y++ )
    {
        for( int x = 0; x < columns; x++ ) // hier wird imageStart immer um ein ganzes Pixel weiter geschoben
        {
            setPixel(imageIterator.r, imageIterator.g, imageIterator.b);
            imageIterator++;
        }
    }
    

    Hier ist jetzt nur noch eine Addition plus Addressbildung mit offset (in der Struktur). Das ist in der Regel deutlich schneller. Die setPixel Methode muss nicht geändert werden. DAs funktioniert so natürlich nur, wenn die Daten hintereinander im Speicher liegen.



  • Das dumme ist nur, dass die Daten sehr unterschiedlich organisiert sein können.
    Der Hersteller kennt ~20 Verfahren wie die Daten organisiert sein können.
    Selbst wenn ich es auf 3 sinnvolle reduziere (8 mono, 10 mono, 8 rgb),
    dann ist es nicht möglich das mit nur einer Struktur zu umzusetzen.
    und insbesondere die 10 Bit Strukturen machen Problem. Ansonsten hast du natürlich recht.



  • Du kannst Dir ja entsprechende Strukturen mit Zugriffsoperatoren anlegen. Dann hast Du eine einheitliche Schnittstelle und der Rest des Codes bleibt gleich. Das gilt für den von Dir beschriebenen Code ja ähnlich. Da muss ja in pixelToRGB() im switch-Statement jeweils eigener Code implementiert werden.

    Statt dessen könntest Du Dir eine Factory bauen, die die entsprechenden Klassen zusammenbaut und Dir zurückliefert. Das macht natürlich nur dann Sinn, wenn der Code im Moment noch nicht schnell genug ist.

    Ich habe beide Versionen bei mir mal ausprobiert (VisualStudio2008, ReleaseBuild):
    Mit Multiplikation auf 5MPixel großem Int-Array: 110ms mit Addition und Strukturen: 15ms

    Hier der Code:

    const int m_size_x = 1024;
    const int m_nSizeX = 1024;
    const int m_nSizeY = 1024;
    
    const int chunkSz = 5*m_nSizeX*m_nSizeY;
    int largeChunk[chunkSz];
    
    char red[chunkSz];
    char green[chunkSz];
    char blue[chunkSz];
    
    void pixelToRGB(char* pPixelPointer, int  & r, int & g, int & b)
    {
        r = static_cast<int>(*(reinterpret_cast<char*>(pPixelPointer+2)));
        g = static_cast<int>(*(reinterpret_cast<char*>(pPixelPointer+1)));
        b = static_cast<int>(*(reinterpret_cast<char*>(pPixelPointer+0)));
    }
    
    int ArrPos(int x, int y)
    {
        // y + height * x
        return (x + m_size_x * y);
    } 
    
    void setPixel(int x, int y, int r, int g, int b)
    {
        int pos = ArrPos(x,y);
        red[pos] = r;
        green[pos] = g;
        blue[pos] = b;
    }
    
    void checkPerfMult(void)
    {
        int m_nBytesPerPixel = 4;
        int nPitch = 4;
        void* pMemVoid = (void*)largeChunk;
        char* pPixelPointer;
        for ( int x = 0; x < m_nSizeX; x++ ) 
        {
            for ( int y = 0; y < m_nSizeY; y++ ) 
            {
                // (x*bytes) + (y * sizex)
                int r;
                int g;
                int b;
                long lMemoryPixelOffset = ((long)x * m_nBytesPerPixel) + ((long)y * nPitch );
                pPixelPointer = (reinterpret_cast<char*> (pMemVoid) + (lMemoryPixelOffset)) ;
    
                pixelToRGB(pPixelPointer, r, g, b);
                setPixel(x,y,r,g,b);
            }
        } 
    }
    
    void* getImageStart(void)
    {
        return largeChunk;
    }
    
    void checkPerfAdd(void)
    {
        // Diese struct muss der organisation eines Pixels im Speicher entsprechen
        struct RGBPixel
        {
            unsigned char r;
            unsigned char g;
            unsigned char b;
            unsigned char x; // unused, 32bit alignment
        };
    
        // jetzt der Zugriff
        RGBPixel* imageIterator = reinterpret_cast<RGBPixel*>(getImageStart()); // Hier den Start des Bildes im Speicher bestimmen
        for( int y = 0; y < m_nSizeY; y++ )
        {
            for( int x = 0; x < m_nSizeX; x++ ) // hier wird imageStart immer um ein ganzes Pixel weiter geschoben
            {
                setPixel(x, y, imageIterator->r, imageIterator->g, imageIterator->b);
                imageIterator++;
            }
        } 
    }
    
    int main( void )
    {
    	for(int i = 0; i<sizeof(testCases)/sizeof(testPattern); i++)
    	{
    		testCases[i].tstPowOfTwo();
    	}
        {
            Timer t;
            checkPerfMult();
        }
        {
            Timer t;
            checkPerfAdd();
        }
    }
    

    Die Timer-Klasse misst die Zeit per clock() . Bei Bedarf kann ich den Code posten.



  • Habs mal mit 100 Durchläufen nachgemessen:

    11,027s zu 0,313s. Das ist doch signifikant.



  • Hi,..

    Für so etwas gibt es die "Streaming Single Instruction Multible Data Extension" kurz SSE. Oder per GPGPU mittels VBO's.

    Damit kannst du Multiplikationen um etliches beschleunigen.
    Bei gängigen Graka's kommst Du damit bis auf 300 GFlops,... ( 300 * 10^9 floating point operations per second)

    Grüße



  • Das ist für Adressberechnungen aber mit Eulen auf Spatzen geschossen (oder Kanonen nach Athen getragen)

    Intelligente Algortihmen bringen mehr (s. mein Benchmark)



  • gastgast schrieb:

    Intelligente Algortihmen bringen mehr (s. mein Benchmark)

    nicht ganz weil:
    Wenn du es kombinierst, erreichst du einen deutlich höheren Faktor mehr weil mit SSE(1,2,3,4.x) und insbesondere GPGPU deutlich mehr aus gleichbleibendem System und Code herauszuholen ist.



  • Wenn du es kombinierst, erreichst du einen deutlich höheren Faktor mehr weil mit SSE(1,2,3,4.x) und insbesondere GPGPU deutlich mehr aus gleichbleibendem System und Code herauszuholen ist.

    Für allgemeine Verarbeitung gilt das ja, aber im Thread geht es nur um Adress- und Kopieroperationen.



  • ogni42 schrieb:

    Für allgemeine Verarbeitung gilt das ja, aber im Thread geht es nur um Adress- und Kopieroperationen.

    Ich könnte wetten dass durch zuschalten von SSE sich doch mehr rausholen lässt.



  • ogni42 schrieb:

    Wenn du es kombinierst, erreichst du einen deutlich höheren Faktor mehr weil mit SSE(1,2,3,4.x) und insbesondere GPGPU deutlich mehr aus gleichbleibendem System und Code herauszuholen ist.

    Für allgemeine Verarbeitung gilt das ja, aber im Thread geht es nur um Adress- und Kopieroperationen.

    Ich erinnere mich an einen Artikel in der Ct wo über die Performance von GPU Algorithmen geredet wurde. Und ein maßgeblicher Faktor war dabei die Zeit die benötigt wird die Daten in die GPU zu bringen. Solange sich hier nicht die gesamte Arithmetik auf Matrizen und massive parallel Verarbeitung abbilden lässt lohnt es sich daher gar nicht GPU zu verwenden.

    Bei SSE weiß ich nichteinmal wie man das programmiert.

    Da ich aber in wesentlichen Datenstrukturen in andere Datenstrukturen abbilde und eigentlich gar nicht rechnen will, denke ich das Vektor/Matrizen Algorithmen der falsch Ansatz sind.



  • pospiech schrieb:

    Ich erinnere mich an einen Artikel in der Ct wo über die Performance von GPU Algorithmen geredet wurde. Und ein maßgeblicher Faktor war dabei die Zeit die benötigt wird die Daten in die GPU zu bringen. Solange sich hier nicht die gesamte Arithmetik auf Matrizen und massive parallel Verarbeitung abbilden lässt lohnt es sich daher gar nicht GPU zu verwenden.

    Matrizen und Vektoren sind ja nichts anderes als Schleifenkonstrukte.

    for( int y = 0; y < m_nSizeY; y++ )
        {
            for( int x = 0; x < m_nSizeX; x++ ) // hier wird imageStart immer um ein ganzes Pixel weiter geschoben
            {
                setPixel(x, y, imageIterator->r, imageIterator->g, imageIterator->b);
                imageIterator++;
            }
        }
    

    Dieser Teil lässt sich mMn sehr gut parallelisieren.



  • Dieser Teil lässt sich mMn sehr gut parallelisieren.

    Ja, aber nicht mit SSE und schon gar nicht mit GPGPU.

    Mit SEE macht es keinen Sinn, da in die SSE Register ein komplettes Wort gelegt werden muss und als Ergebnis aber kein weiteres vollständiges Datenwort heraus kommt, sondern auf drei unterschiedliche Worte verteilt wird.

    Mit GPGPU macht das keinen Sinn, weil es ja nur um das Umkopieren der Daten geht und die liegen nun mal im Hauptspeicher des Rechners und nicht im Speicher der GPU. GPGPU beschleunigt im Wesentlich Matrixoperationen wie MulAdd, Inversion etc.

    Alternative wäre, die Schleife zu entrollen (z.B. mit OpenMP). Das kann, muss aber nicht zu schnellerer Verarbeitung führen. Das hängt von der Organisation der Caches ab. Dazu gibt es ein gutes Paper "What every programmer should know about memory".



  • ogni42 schrieb:

    Dieser Teil lässt sich mMn sehr gut parallelisieren.

    ....

    Diese Antwort bezog sich nur auf die Schleifen nicht auf SSE oder GPGPU

    Ja muss man halt durchprobieren, gibt ja nicht nur openmp..



  • Hi,...

    Also die doppelten schleifen durchläufe brauchen Vieeeel zu viel zeit,...
    Die Zeilen liegen bei beiden doch so oder so hintereinander im Speicher
    Und wie ich das verstanden habe muss nur ein Byte, und zwar x, entfernt werden.

    struct RGBX
    { 
      u8 r;
      u8 g;
      u8 b;
      u8 x;
    };
    
    struct RGB
    {
      u8 r;
      u8 g;
      u8 b;
    };
    
    RGBX* startmemRGBX= GetStartOfRGBX();
    RGB*  startmemRGB = GetStartOfRGB();
    
    size_t RGBXIndex =0;
    size_t RGBIndex  =0;
    
    for(int i=0; i<pixelanzahl; i++)
    {
    memcpy_s(startmemRGB+RGBIndex,sizeof(RGB),startmemRGBX+RGBXIndex,sizeof(RGBX));
    
    RGBXIndex++;
    RGBIndex++;
    };
    

    grüüße


Log in to reply