Wie memcpy für bool array nutzen? (QT)

  • C++

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von Renati.

    Wie memcpy für bool array nutzen? (QT)

    Hey,

    ich programmiere grad ne Simulation für das Ising Modell.
    Ich habe eine Matrix mit Werten, die nur 2 Zustände annehmen können.
    Bisher habe ich dafür aus verschiedenen Gründen ein vector < vector <unsigned char> > verwendet. (unsigned char** ginge genauso)

    Das ganze wird dann mit QT visualisiert. Dazu verwende ich ein QImage mit Format Format_Indexed8.
    In dem Format gibts ne Farbtabelle und eine Matrix aus chars, in der die Farbindizes gespeichert werden ( 256 mögliche Farben).
    So kann ich ganz einfach den Inhalt der Werte-Matrix mit memcpy in die Index-Matrix zeilenweise reinkopieren.
    Das sieht dann so aus:

    Quellcode

    1. for (int y = 0; y < N; y++)
    2. memcpy(image->scanLine(y), &(data->matrix[y][0]), N);


    Man kann über scanLine den Pointer des ersten Indexes einer Pixelzeile rausfinden.
    Wenn man mit zwei for Schleifen den Pointer des Elements (x,y) dereferenziert und den Wert setzt dauerts 50 mal so lange. Deswegen memcpy.
    Speicherplatz war bisher kein Thema, da ich neben der char Matrix auch eine gleichgroße Matrix mit 64bit integers hatte. Deswegen wars egal ob ich für die Werte ein byte (char) oder ein bit (bool) belege.
    Jetzt hab ich aber nen Algorithmus gefunden, bei dem ich nur 2 Matrizen mit bool Werten brauche.
    Die Frage ist, wie bekomm ich den Inhalt von nem vector<bool> oder von nem bool array effizient in das char array von dem Bild rein?
    image->scanLine() liefert immer den Pointer auf das erste Byte. Wählt man statt Format_Indexed8 das Format Format_Mono, so sind in dem byte die ersten 8 Pixel drin und die Farbtabelle hat nur noch 2 Farben.
    Bist du dir sicher, dass du weißt was memcpy macht?
    cplusplus.com/reference/cstring/memcpy/

    Ich gehe davon aus, dass N die Anzahl der Zeilen ist.
    Laut deinem Code ist eine Zeile auch immer N lang.
    In dem Beispiel würde ich das anders angehen(bin aber kein c++ programmierer)
    Spoiler anzeigen

    Quellcode

    1. int height = N;
    2. for(int y = 0; y < height; y++)
    3. {
    4. vector<bool> line = image->scanLine(y);
    5. for(int x = 0; x < line.size(); x++)
    6. {
    7. data->matrix[y][x] = line[x] ? 'Y' : 'N';
    8. }
    9. }


    Der Code ist sicher nicht perfekt. Hab auch schon ewig nix mehr mit C++ gemacht und weiß auch nicht was die STL usw. so bietet. Jedoch müsste es so ca. funktionieren.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Ich empfehle Dir, das Array

    C-Quellcode

    1. data->matrix[dimY][dimX]
    nicht 2-Dimensional, sondern eindimensional anzulegen, das ist einfacher und performanter.

    C-Quellcode

    1. data->matrix[dimY * dimX]
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @thefiloe
    Ja die Matrix ist immer eine NxN Matrix insofern ist sowohl die Zeilenlänge, als auch die Anzahl der Zeilen gleich.
    Wenns rechteckig und nicht quadratisch wäre wüsste ich schon wo ich was ändern würde.

    Das Problem ist nicht das kopieren der Werte von nem bool array in ein char array, sondern das effiziente kopieren.
    Bisher war die memcpy Variante ziemlich effizient und zwar genau 50 mal schneller als das Setzen jedes einzelnen Pixels.
    Dies war aber nur möglich weil ich ein char array blockweise per memcpy in ein anderes char array kopieren konnte.
    Jetzt muss der Inhalt von nem bool array/ bool vector in das char array.
    Denn scanline() liefert immer nen Pointer auf char, auch wenn (wie bei Format_Mono) die Pixel bitweise in ein Byte gepackt werden (8 Pixel in einem byte).

    @RodFromGermany
    Wieso ist data->matrix[dimY * dimX] performanter? Ich habe es bisher aus Bequemlichkeit und Übersichtlichkeit einen 2D vector genommen.
    Ich hätte geraten, es ist Wurscht wie Käse, ob 1D oder 2D. Mit ist klar, dass in ersterem Fall es nur ein kontinuierlicher Speicherblock ist.

    €:
    Die einfachste Variante wäre es mit image->setPixel(x, y, data->matrix[y][x]) jeden Pixel einzeln zu setzen. Das dauert natürlich sehr viel länger als memcpy, vor allem weil man jedesmal nur 1 Bit setzt. (Wahrscheinlich nochmal Faktor 8 langsamer)

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „markus.obi“ ()

    Geht mein Beispiel nicht?
    Wenn ja was?
    Ach ja @RodFromGermany du kannst nicht davon ausgehen, dass das nen 2D-Array ist. In C++ kannste den Indexoperator überladen.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.

    thefiloe schrieb:

    Geht mein Beispiel nicht?

    Also das Beispiel lößt nicht das Problem, weil ich wegen der Geschwindigkeit den Bereich gleich blockweise kopieren will.

    Davon abgesehen weiß ich gar nicht was du mit deinem Code überhaupt machen willst.
    Was soll dabei rauskommen, wenn du dem vector line einen Wert vom Typ char* zuweißt? Das kompiliert nicht mal.

    Quellcode

    1. vector<bool> line = image->scanLine(y);


    Eine Möglichkeit wäre über 2 for Schleifen (ist natürlich langsam)

    Quellcode

    1. unsigned int height = 10;
    2. unsigned int width = 10;
    3. for (unsigned int y = 0; y < height; y++)
    4. for (unsigned int x = 0; x < width; x++)
    5. {
    6. *(Viewer->image->scanLine(y) + x) = data->matrix[y][x]; // Erhöht den Pointer um x, Impliziter Cast von bool nach unsigned char
    7. //Oder
    8. Viewer->image->setPixel(x , y, data->matrix[y][x]); // Impliziter Cast von bool nach unsigned int
    9. }


    Was mir helfen würde, wäre die Speicheradresse vom ersten byte eines vector<bool>/ bool array.
    Also ein Pointer auf char in dem sich die ersten 8 bit befinden. Da ginge memcpy.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „markus.obi“ ()

    markus.obi schrieb:

    Wieso ist data->matrix[dimY * dimX] performanter?
    Ich hab Bildverarbeitung so und so programmiert, so war es schneller.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany
    Ich vermute mal, dass das daran liegt, dass dein Bild "relativ" klein war. Dadurch haste durch die Cacheline nen Vorteil, wenn du zeilenweise drüber iterierst. Beim letzten Element einer Zeile haste dann automatisch schon die ersten Elemente der nächsten Zeile im Cache. Bei mir gehts um Matrizen/Bilder größer 20000^2. Ich glaub nicht, dass das da noch nen großen Unterschied macht.

    markus.obi schrieb:

    Beim letzten Element einer Zeile haste dann automatisch schon die ersten Elemente der nächsten Zeile im Cache.
    Darf ich davon ausgehen, dass Du von Bildverarbeitung keine Ahnung hast?
    Da laufen z.B. Operatoren einer nicht konstanten Fläche über das Bild.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Mal ne Frage. Was willst du immer mit memcpy. Ich kenn mich zu wenig aus, als das ich weiß was nen bool im speicher ist. Aber gehen wir mal z.B. davon aus, dass nen true den Wert 1 hat und nen false den Wert 0. Wenn du da mit memcpy kommst und den Speicher kopierst, haste anschließend entweder ne 1 oder ne 0 in deinem char. Nur ist, das dann kein '1' sondern ne 1. Das heißt da würde dann das dort stehen: ☺ und bei 0 gar nix. Wie gesagt ich weiß nicht wie das Zeug im Speicher aussieht. Aber nen schönes Ergebnis bekommste sicher nicht.
    Außerdem bei solch EXTREMEN Datenmengen würde ich mir eher sorgen um QT machen.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Ich weiß was du meinst. Die Uint's 0-9 sehen in Binärform anders aus als die Chars '0' - '9'.
    Das sollte allerdings keine Rolle spielen, weil bei memcpy z.B. nie eine Umwandlung stattfindet.
    Man müsste einfach nur die Speicheradresse des ersten bytes eines vector<bool> rausfinden. Sozusagen ein reinterpret cast des bool pointers nach char pointer (mir ist klar das, dass byte 8bit hat und bool in nem vector<bool> nur 1bit ist).
    Bin mir nicht sicher ob das geht.

    thefiloe schrieb:

    Was willst du immer mit memcpy

    Ganz einfach: Die memcpy Variante ist Faktor 50 schneller (bei ner char Matrix) als die "normalen" Varianten aus Post#6
    Mit scanline oder setPixel dauert 3-4 Sekunden und mit memcpy nur ~0.07 Sekunden. (bei 20000*20000)

    @RodFromGermany
    Ja, ich habe keine Ahnung von Bildverarbeitung. Aber hier gehts weniger um Bildverarbeitung als um die Darstellung eines Bildes. Genauer gesagt die schnellst möglichste graphische Darstellung einer NxN 2D Matrix mit bool-Werten (vetctor/array).

    Der vector<bool> hat übrigens ne Sonderstellung unter allen vector Klassen. Er verhält sich leicht anders. (Ich sehe grad, dass die 8bool/byte Speicherung nicht garantiert ist)
    Ja und?
    x mal memcpy() mit x Werten
    oder
    1 mal memcpy() mit x ^ 2 Werten
    Das ist hier die Frage.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Natürlich ist memcpy sehr schnell, da dies auch pur in asm geschrieben worden ist und dadurch optimal optimiert wurde.
    Außerdem wollt grad sagen... bool 1 bit? Soviel ich weiß geht das nicht. Wenn du nen bool array hättest wäre ja z.b. eine Adresse(natürlich nicht wirklich) 0xFA+1/8?

    Aber wenn du ja sogar schon Zeitwerte hast, ist meine Frage was nicht funktioniert?


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.

    thefiloe schrieb:

    Aber wenn du ja sogar schon Zeitwerte hast, ist meine Frage was nicht funktioniert?

    Der Zeitwert bezieht sich auf ne Matrix aus char. Da verwende ich bereits memcpy. Jetzt wollte ich auf vector<bool> umsteigen, um den Speicherplatz optimieren, um noch größere Systeme zu simulieren.
    Dazu hatte ich vor die 8bool/byte Speicherung zu nutzen, um den minimalen Speicherbedarf zu erreichen. (Keine Sorge die Laufzeit der Simulation wächst langsamer als N^2)

    thefiloe schrieb:

    Außerdem wollt grad sagen... bool 1 bit? Soviel ich weiß geht das nicht. Wenn du nen bool array hättest wäre ja z.b. eine Adresse(natürlich nicht wirklich) 0xFA+1/8?

    cplusplus.com sagt (link oben):
    The storage is not necessarily an array of bool values, but the library implementation may optimize storage so that each value is stored in a single bit.

    Die komprimierte Speicherung ist also erlaubt aber nicht garantiert. Bool Arrays sind glaub ich immer 1bool/byte.

    Wie das bei vector<bool> mit den Adressen abläuft weiß ich nicht.
    Kann sein, dass er das bit für bit in zahlen ablegt. Aber das wird dann nahezu unmöglich via memcpy das wieder zu extrahieren und in chars zu packen.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Mich interessiert nicht nur das Ergebnis der Simulation, sondern auch die Zwischenschritte. Ich will dabei zuschauen wie sich das System entwickelt.
    Deswegen wird in regelmäßigen Abständen die Matrix visualisiert.
    Ich denke ich werde bei dem vector<char> bleiben. Der Zugriff auf ein Element eines bit vectors dauert bei der kompakten Speicherung auch länger.
    Man tauscht Speicherplatz gegen CPU Zeit. Da ich verhältnismäßig viele Zugriffe habe lohnt sich die kompakte Speicherung nicht wirklich.
    Das dachte ich mir schon. Trotzdem scheint es mir völlig überflüssig, immer die ganze Matrix in den Bildspeicher zu kopieren, wenn die Änderungsmatrix nur verhältnismäßig dünn besetzt ist. Und wenn das nicht der Fall ist, kann man mit etwas mehr Aufwand das Kopieren ganz sein lassen.
    Außerdem solltest du (wie von RodFromGermany bereits angemerkt) keine Vektoren "verschachteln", wenn du sehr auf Geschwindigkeit achten willst. Doppelt dereferenzieren kostet Zeit. Caching funktioniert dann besser. Iteratoren sind effizienter zu benutzen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Renati“ ()