3d array sehr schnell in Bitmap. Warum mit Scanline nicht schneller ?



  • Hallo,

    ich möchte aus einem 3d array z.B. arraySat[1000][1000][36] möglichst schnell ein Bitmap erstellen. Mit Canvas->Pixels ist das sehr langsam. Habe gelesen, dass das ganze mit Scanline deutlich schneller funktionieren soll, leider ergibt sich bei mir daraus kein Geschwindigkeitsvorteil. Habe ich etwas falsch gemacht bei der Anwendung von Scanline. Gibt es eine Möglichkeit ein Array in einem Zug ohne Schleife in ein Bitmap zu transformieren.

    MFG

    Marc

    Graphics::TBitmap *bmp_array = new Graphics::TBitmap();
    bmp_array->Height      = zeile;
    bmp_array->Width       = spalte;
    bmp_array->PixelFormat = pf24bit;
    RGBTRIPLE* t;
    
      for(int y = 0; y < zeile; y++)
      {
        t = (RGBTRIPLE*)bmp_array->ScanLine[y];
        for(int x = 0; x < spalte; x++)
        {
          t->rgbtBlue = (BYTE)arraySat[y][x][band1];
          t->rgbtGreen = (BYTE)arraySat[y][x][band2];
          t->rgbtRed = (BYTE)arraySat[y][x][band3];
          t++;
        }
      }
    


  • Du könntest versuchen, die Dimensionen des Arrays zu vertauschen, um evtl. eine besser Cache Auslastung zu erreichen. Aber vermutlich ist bei diesem byteweisen herumkopiere nicht viel zu machen, wirklich schnell können nur ausgerichtete 4 Byte-Blöcke kopiert werden, daher vermutlich der Scanline-Tipp. Und selbst dann hat es die CPU schwer mal eben Millionen von Pixeln umzukopieren.

    Bye, TGGC (Dem beste BdT)



  • Normalerweise sollte ein Zugriff per ScanLine schneller sein, alleine deswegen, weil er bei jedem Pixel[]-Zugriff die Pixel-Adresse neu aus den Koordinaten berechnet, anstatt einfach einen Zeiger zu inkrementieren.

    Unabhängig von Scanline erzielt man bei solchen Vorgängen auf Vektoren oft erhebliche Gewinne, wenn man die Loops "unrollt", um den Compiler bei der Parallelisierung zu unterstützen. Also in etwa:
    [cpp]
    u=t+1;
    for(int x = 0; x < spalte; x=x+2 )
    {
    t->rgbtBlue = (BYTE)arraySat[y][x][band1];
    t->rgbtGreen = (BYTE)arraySat[y][x][band2];
    t->rgbtRed = (BYTE)arraySat[y][x][band3];
    u->rgbtBlue = (BYTE)arraySat[y][x+1][band1];
    u->rgbtGreen = (BYTE)arraySat[y][x+1][band2];
    u->rgbtRed = (BYTE)arraySat[y][x+1][band3];
    t=t+2;
    u=u+2;
    }
    // natürlich vorher gucken, ob "spalte" gerade Zahl ist, usw.
    [/cpp]
    Das wäre ein "Unroll-Faktor" von 2, weil pro Schleifendurchlauf zwei Vektor-Elemente bearbeitet werden. Zumindest bei größeren Feldern solltest du einen erheblichen Unterschied messen können. Probier' einfach mal unterschiedliche Unroll-Faktoren aus.

    Google mal nach loop unrolling. Wichtig ist dabei, dass die Operationen innerhalb der Schleife voneinander unabhängig sind (und vom Compiler auch so erkannt werden), so dass sie gefahrlos parallelisiert werden können.

    Na ja. Das können andere sicherlich besser erklären als ich.



  • Mich würden die Ergebnisse interessieren: Mess doch einfach mal n Durchläufe für die verschiedenen Lösungsansätze! (Posten!)



  • Hallo,

    so komme gerade vom Umzug wieder und habe das mal schnell probiert mit den Unroll-Faktoren. Hat leider bisher noch keinen Geschwindigkeitsvorteil gebracht. Werde mal ein wenig zu dem Thema Googlen.

    Vielleicht hat ja noch jemand einen anderen Vorschlag.

    Grüße Marc



  • Einfach den ganzen Block mit memcpy rüberrkopulieren?



  • Mein Problem ist, daß ich ein 3d-Array habe welches nicht linear erstellt wurde, sondern per Schleife eingeladen wurde.

    // Bereitstellen des PLatzes fuer die m int-Zeiger ( = zeilen)
    matrix3db = new unsigned char **[zeile];
    for (i_image = 0; i_image < zeile; i_image++)
      {
        // Bereitstellen des Platzes für die n Spalten der i.ten zeile
        matrix3db[i_image] = new unsigned char*[spalte];
        }
    
      for (i_image2 = 0; i_image2 < zeile; i_image2++)
      { for (i_image3 = 0; i_image3 < spalte; i_image3++)
      {
    
        // Bereitstellen des Platzes für dim
        matrix3db[i_image2][i_image3] = new unsigned char[dim];
    

    Wenn ich jetzt einzelne Bänder (z. B. [zeile][spalte][2], [zeile][spalte][4] ) daraus per memcpy kopieren möchte funktioniert das wohl nicht. Sprich, ich müßte meine Speicherfreigabe ändern und das ganze linear einlesen. Das wäre aber sehr unbefriedigend, derweilen meine ganzen anderen Routinen und die Daten auf diese Speicherverwaltung ausgerichtet sind. 😮
    oder gibt es eine Möglichkeit auch auf den wie oben beschriebenen Speicherblock den memcpy Befehl auszuführen

    Grüße Marc



  • naja, wenn du sauber die oop durchgehalten hast, sollte es kein thema sein die zugrunde liegende datenstruktur (hier deine matrix) auf ein anderes format umzustellen.

    du kannst hier immer nur die 3. dimension per memcpy herumkopieren. desweiteren solltest du auf zeiger-arithmetik umsteigen um die verwendung der index-operatoren ([]) zu verhindern.


Anmelden zum Antworten