2015-07-15 10 views
5

Dies ist eine Art Follow-up zu dieser ursprünglichen Frage mit einigen neuen Informationen hinzugefügt. Sehen Sie hier für den ersten Teil, wenn Sie interessiert sind: Struct of arrays, arrays of structs and memory usage patternStruktur der Arrays und Speicherzugriffsmuster

Es scheint, dass es einige Probleme mit meinem ersten Versuch gibt, Struktur von Arrays für eine einfache Klasse einzurichten. Hauptsächlich übermäßige Speicherzuweisung für Zeiger und mögliche Speicherlecks beim Zuordnen dieser Zeiger von vec3_b in der vorherigen Frage.

Ich dachte, wie ich die Daten ohne Pointer neu anordnen könnte, dies erfordert, dass ich zunächst einige Const-Variable für die Größe meiner Daten Buckets, also keine unbegrenzten Werte wie Zeiger, sondern auch die Menge an Speicher auf etwas repariert.

const size_t batch_size = 100; 
    struct vec3_c 
    { 
    size_t x[batch_size]; 
    size_t y[batch_size]; 
    size_t z[batch_size]; 
    }; 

    struct vec3_c vec3_c(size_t x, size_t y, size_t z, size_t index) 
    { 
     struct vec3_c v; 
     v.x[index] = x; 
     v.y[index] = y; 
     v.z[index] = z; 
     return v; 
    } 

     struct vec3_c vc3;   
     for(int i = 0; i < batch_size; i++) 
     { 
      vc3 = vec3_c(i+1, i*i, i*10, i); 
      //printf("vec3c x:%zu, y:%zu, z:%zu\n",vc3.x[i], vc3.y[i], vc3.z[i]); 
      printf("vec3c x:%p, y:%p, z:%p\n",(void*)&vc3.x[i], (void*)&vc3.y[i], (void*)&vc3.z[i]); 
     } 

     ---------------x-----------------|----------------y-----------------|----------------z-----------------| 

0|  0x7fff57489f40 : 140734657765184 | 0x7fff5748a260 : 140734657765984 | 0x7fff5748a580 : 140734657766784 
1|  0x7fff57489f48 : 140734657765192 | 0x7fff5748a268 : 140734657765992 | 0x7fff5748a588 : 140734657766792 
2|  0x7fff57489f50 : 140734657765200 | 0x7fff5748a270 : 140734657766000 | 0x7fff5748a590 : 140734657766800 

mit diesem aktualisierten Code ich eine feste Behältergröße haben muß, damit ich es eingestellt nur für einfache Zahlen batch_size von 100. Füllen Sie das vec3c mit einigen Daten und führte einen ähnlichen Test durch, dieses Mal scheint es, dass jeder Wert in 8 Byte-Blöcken ausgerichtet ist.

ex:

size of vec3  : 24 bytes 
size of vec3a  : 24 bytes 
size of vec3b  : 24 bytes 
size of vec3c  : 2400 bytes 
size of size_t : 8 bytes 
size of int  : 4 bytes 
size of 16 int : 64 bytes 
vec3c x:0x7fff592d2f40, y:0x7fff592d3260, z:0x7fff592d3580 
vec3c x:0x7fff592d2f48, y:0x7fff592d3268, z:0x7fff592d3588 
vec3c x:0x7fff592d2f50, y:0x7fff592d3270, z:0x7fff592d3590 
vec3c x:0x7fff592d2f58, y:0x7fff592d3278, z:0x7fff592d3598 
vec3c x:0x7fff592d2f60, y:0x7fff592d3280, z:0x7fff592d35a0 
vec3c x:0x7fff592d2f68, y:0x7fff592d3288, z:0x7fff592d35a8 
vec3c x:0x7fff592d2f70, y:0x7fff592d3290, z:0x7fff592d35b0 
vec3c x:0x7fff592d2f78, y:0x7fff592d3298, z:0x7fff592d35b8 
vec3c x:0x7fff592d2f80, y:0x7fff592d32a0, z:0x7fff592d35c0 
vec3c x:0x7fff592d2f88, y:0x7fff592d32a8, z:0x7fff592d35c8 

alle durch 8 Bytes getrennt sind.

Das sollte die Probleme von Speicherlecks und den überschüssigen Speicher für die Zeiger loswerden.

mit diesem neuen Layout würde etwas wie sizeof (vc3 [0] .x) 8 Bytes zurückgeben.

zurück zu den ursprünglichen Fragen:

  1. Ist meine Implementierung von struct vec3_c der richtige Weg, eine Struktur von Array-Setup?

  2. mit einer Vec_3c Batchgröße von 100 zeigt es 2400 Bytes groß aber jedes einzelne Element ist nur 8 Bytes und richtig ausgerichtet, so dass ich jetzt tatsächlich 8 Elemente auf 1 moderne CPU-Cache-Zeile passen konnte?

  3. würde die Umwandlung von Daten, die mir übergeben werden, in einem typischen Format von nur Arrays von Strukturen die Leistungsvorteile überwiegen, da sie in einem Cache-freundlichen Zustand sind und mehrere Datenpunkte pro Instruktionsaufruf verarbeiten können? Dies ist mit der Einschränkung, dass beide Punkte 1 und 2 korrekt sind.

ex tun das Skalarprodukt zweier Vektoren: , das bedeutet, dass ich das Skalarprodukt 2 vec3_c pro Befehlszyklus bekommen?

bearbeiten eine Frage, wäre es besser, die zusätzlichen 8 Byte Daten hinzufügen, um ein Vielfaches von 32 Bytes machen diese Struktur und vielleicht nutzen, dass weitere 8 Byte als Scratch-Platz oder es einfach leer lassen?

bearbeiten Es wurde mir darauf hingewiesen, dass meine anfängliche Initialisierungsfunktion nur ein Chaos der Dinge machte.Ich kann es auf diese Form aktualisiert:

struct vec3_c* vec3_c() 
{ 
    struct vec3_c *v = (struct vec3_c*)malloc(sizeof(struct vec3_c)); 
    v->index = 0; 
    return v; 
} 

struct vec3_c* v3 = vec3_c(); 
    for(size_t i = 0; i < batch_size; i++) 
    { 
     v3->x[i] = i + 1; 
     v3->y[i] = i * i; 
     v3->z[i] = i * 10; 
     printf("index:%d\tvec3c x:%zu, y:%zu, z:%zu\n",i,v3->x[i], v3->y[i], v3->z[i]); 
     printf("index:%zu\tvec3c x:%p, y:%p, z:%p\n",i,(void*)&v3->x[i], (void*)&v3->y[i], (void*)&v3->z[i]); 
    } 
+2

Sorry, vielleicht interpretiere ich das ganze Problem falsch, aber es scheint mir, dass Sie vc3 und v falsch verwenden: die Funktion vec3_c reserviert ein lokales (nicht initialisiertes) "v", setzt dann einige Felder und gibt eine Kopie zurück Of v (wessen Felder nicht vorhersagbare Werte enthalten, außer für den Index). Ich sehe, dass Sie in Ihrem Beispiel nicht wirklich an dem Inhalt von vc3 interessiert sind. Was ist der Zweck der Funktion vec3_c? –

+0

@GiuseppeGuerrini mein ursprüngliches Codebeispiel hatte ein paar Fehler, die ich im Laufe der Zeit aussortierte. Ich würde diese Struktur verwenden, um vec3-Werte in einer Stream-Verarbeitung oder SIMD-Anweisungen in Stapelverarbeitung zu verarbeiten. – user1610950

Antwort

3

Wenn Sie viele dieser xyz Punkte gehen zu müssen, und Sie wollen in der Lage sofort eine Aktion auf allen xs auszuführen, dann macht es mehr Sinn zu setzt alle xs zusammen:

struct PointBatch { 
    size_t x[batchsize]; 
    size_t y[batchsize]; 
    size_t z[batchsize]; 
} 

// More efficient for things like 
// - find the point with the largest X 
// - find the sum of all the points as [xsum, ysum, zsum] 

Wenn Sie in der Regel auf der x arbeiten, y und z einzelnen Datenpunkte, dann macht es mehr Sinn, jeden Punkt zusammen als eine Struktur zu setzen.

N.B.
Beachten Sie, dass die Wertentwicklung nicht ein Thema ist, werden Sie wahrscheinlich, dass der leichte Point/pointBatch Ansatz macht den Code finden, zu schreiben und besser lesbar als struct PointBatch Ihnen keine bequeme Möglichkeit gibt zu beziehen oder einen einzigen Punkt passieren herum.

+0

Die Daten werden höchstwahrscheinlich in Form von Struct A an mich übergeben, aber um sie effizienter in meinen Algorithmen zu verarbeiten, muss ich einmal von A nach B umwandeln und B speichern, um sie stapelweise zu verarbeiten. Ich habe mich in der ursprünglichen Frage darauf bezogen, dass die Transformationskosten einmal im Voraus gegenüber dem Batching für Cache-Kohärenz und SIMD-Style-Verarbeitung gefragt wurden. – user1610950

+0

Eigentlich, wenn Sie alle Punkte nacheinander durchlaufen müssen, ist eine struct-of-arrays viel besser für SIMD. Das Beispiel "x^2 + y^2

+0

Danke, das ist die Richtung, in die ich mich begebe, um die Batch-Verarbeitung der Daten vom SIMD-Typ zu machen. Ich werde sie zuerst von AoS zu SoA drehen müssen. – user1610950

2

Es ist zumindest ein Problem mit diesem Teil des Codes:

struct vec3_c vec3_c(size_t x, size_t y, size_t z, size_t index) 
{ 
    struct vec3_c v; 
    v.x[index] = x; 
    v.y[index] = y; 
    v.z[index] = z; 
    return v; 
} 

erstellen Sie eine neue vec3_v Struktur auf dem Stapel und gibt es zurück. Das bedeutet, dass jeder Aufruf eine neue Struktur zurückgibt, bei der alle Elemente nicht initialisiert sind, mit Ausnahme der Zeile index.

Und später, wenn Sie (100 mal) vc3 = vec3_c(i+1, i*i, i*10, i); tun, kopieren Sie 300 Werte (Größe der Struktur), 297 sind nicht initialisiert: das ist wirklich uneffizient und ruft Undefined Behavior!

+0

Es ist tatsächlich der Aufrufer, der Speicherplatz für den Rückgabewert reserviert. In den ABIs x86 und amd64 übergibt der Aufrufer einen Zeiger an diesen Raum. (Und der Angerufene müsste den Konstruktor ausführen, wenn er nicht schon der Konstruktor wäre, also ja, er müsste die Teile, die er nicht geschrieben hat, auf Null setzen. Oder würde das? Der Code verwendet keinen Initialisierer, der das würde Die zurückgegebenen 'vec3_c' sind bis auf' v. * [index] 'nicht initialisiert. Wenn Sie Glück haben, könnte der Compiler das meiste davon eliminieren, nachdem Sie diesen Konstruktor eingefügt haben. –

+0

@PeterCordes: natürlich bist du wahr! Es ist C und nicht C++ ... Und es ist explizit in 6.7.9 10: * Wenn ein Objekt mit automatischer Speicherdauer nicht explizit initialisiert wird, ist sein Wert unbestimmt * –

+0

Auch wenn es C++ war, ohne Null-Arg Konstruktor, wäre es nicht immer noch nicht initialisiert? –

Verwandte Themen