Moderner Ansatz, um std :: vector ausgerichteten Speicher zuzuweisen

11

Die folgende Frage ist verwandt, die Antworten sind jedoch alt, und der Kommentar von Benutzer Marc Glisse legt nahe, dass es seit C ++ 17 neue Ansätze für dieses Problem gibt, die möglicherweise nicht angemessen diskutiert werden.

Ich versuche, den ausgerichteten Speicher für SIMD ordnungsgemäß zu verwenden, während ich weiterhin auf alle Daten zugreifen kann.

Wenn ich unter Intel einen Float-Vektor vom Typ erstelle __m256und meine Größe um den Faktor 8 reduziere, habe ich einen ausgerichteten Speicher.

Z.B std::vector<__m256> mvec_a((N*M)/8);

Auf eine etwas hackige Weise kann ich Zeiger auf Vektorelemente umwandeln, um zu schweben, wodurch ich auf einzelne Gleitkommawerte zugreifen kann.

Stattdessen würde ich es vorziehen, eine zu haben, std::vector<float>die korrekt ausgerichtet ist und somit __m256ohne Segfaulting in und andere SIMD-Typen geladen werden kann.

Ich habe mich mit align_alloc befasst .

Dies kann mir ein Array im C-Stil geben, das korrekt ausgerichtet ist:

auto align_sz = static_cast<std::size_t> (32);
float* marr_a = (float*)aligned_alloc(align_sz, N*M*sizeof(float));

Ich bin mir jedoch nicht sicher, wie ich das machen soll std::vector<float>. Das std::vector<float>Eigentum an zu geben marr_a scheint nicht möglich zu sein .

Ich habe einige Vorschläge gesehen, dass ich einen benutzerdefinierten Allokator schreiben sollte , aber das scheint viel Arbeit zu sein, und vielleicht gibt es mit modernem C ++ einen besseren Weg?

Prunus Persica
quelle
1
ohne Segfaulting ... oder ohne mögliche Verlangsamungen durch Cache-Line-Splits, wenn Sie verwenden _mm256_loadu_ps(&vec[i]). (Beachten Sie jedoch, dass GCC mit den Standardoptimierungsoptionen nicht garantiert ausgerichtete 256-Bit-Ladevorgänge / -Speicher in vmovups xmm / vinsertf128 aufteilt. Daher ist die Verwendung von _mm256_loadover von Vorteil, loaduwenn Sie sich darum kümmern, wie Ihr Code auf GCC kompiliert wird, wenn jemand dies vergisst Verwendung -mtune=...oder -march=Optionen.)
Peter Cordes

Antworten:

1

Alle Container in der Standard-C ++ - Bibliothek, einschließlich Vektoren, verfügen über einen optionalen Vorlagenparameter , der den Allokator des Containers angibt , und es ist nicht wirklich viel Arbeit, einen eigenen zu implementieren:

class my_awesome_allocator {
};

std::vector<float, my_awesome_allocator> awesomely_allocated_vector;

Sie müssen ein wenig Code schreiben, der Ihren Allokator implementiert, aber es wäre nicht viel mehr Code als Sie bereits geschrieben haben. Wenn Sie keine Unterstützung vor C ++ 17 benötigen, müssen Sie nur die Methoden allocate () und deallocate () implementieren .

Sam Varshavchik
quelle
Sie müssen sich auch spezialisierenallocator_traits
NathanOliver
1
Dies könnte ein guter Ort für eine kanonische Antwort mit einem Beispiel sein, das Benutzer kopieren / einfügen können, um durch die nervigen Rahmen von C ++ zu springen. (Bonuspunkte, wenn es eine Möglichkeit gibt, std :: vector zu versuchen, anstelle des üblichen Braindead-C ++ immer neu zuzuweisen + Kopie zuzuweisen.) Beachten Sie natürlich auch, dass dies vector<float, MAA>nicht typkompatibel ist vector<float>(und nicht daran liegen kann) Alles, was .push_backauf einer Ebene ausgeführt wird, std::vector<float>die ohne diesen Zuweiser kompiliert wurde, könnte eine neue Zuordnung vornehmen und in einen minimal ausgerichteten Speicher kopieren. Und new / delete ist nicht kompatibel mit align_alloc / free)
Peter Cordes
1
Ich glaube nicht, dass es eine Garantie dafür gibt, dass der vom Allokator zurückgegebene Zeiger direkt als Basisadresse des std::vectorArrays des Arrays verwendet wird. Zum Beispiel könnte ich mir eine Implementierung vorstellen, bei der std::vectornur ein Zeiger auf den zugewiesenen Speicher verwendet wird, der das Ende / die Kapazität / den Allokator vor dem Wertebereich im Speicher speichert. Dies könnte die Ausrichtung des Allokators leicht vereiteln.
Dietmar Kühl
1
Nur das std::vectorgarantiert es. Dafür wird es verwendet. Vielleicht sollten Sie überprüfen, was der C ++ - Standard hier spezifiziert.
Sam Varshavchik
1
> Sie müssen sich auch spezialisieren allocator_traits- Nein, das tun sie nicht. Sie müssen lediglich einen kompatiblen Allokator implementieren.
Andrey Semashev