Ich versuche zu lernen, mit Intrinsic zu codieren, und unten ist ein Code, der Additionen durchführt
compiler used: icc
#include<stdio.h>
#include<emmintrin.h>
int main()
{
__m128i a = _mm_set_epi32(1,2,3,4);
__m128i b = _mm_set_epi32(1,2,3,4);
__m128i c;
c = _mm_add_epi32(a,b);
printf("%d\n",c[2]);
return 0;
}
Ich bekomme den folgenden Fehler:
test.c(9): error: expression must have pointer-to-object type
printf("%d\n",c[2]);
Wie drucke ich die Werte in der Variablen c was vom Typ ist __m128i
Beachte das auch __m128i hat keine Informationen über den Typ, der gespeichert wird. Es können 8-Bit-Ints, 16-Bit-Ints, 32-Bit usw. sein. Einige Compiler unterstützen die .m128i_i32 Felderweiterungen. Aber es ist definitiv kein Standard und nicht in GCC.
– Mystisch
6. November 2012 um 18:59 Uhr
im Zusammenhang mit dem Titel: Wie drucke ich die __uint128_t-Nummer mit gcc?
– jfs
6. November 2012 um 19:00 Uhr
Beachten Sie, dass einige Compiler eine eingebaute printf-Unterstützung für SIMD-Typen haben, z. B. Apples Versionen von gcc, clang usw., die alle unterstützt werden %vld zum Drucken u __m128i als 4 x 32 bit ints.
– PaulR
6. November 2012 um 19:12 Uhr
Ich verwende den Intel-Compiler
– arunmoezhi
6. November 2012 um 19:15 Uhr
Gibt es eine Möglichkeit, eine maskierte Addition durchzuführen? Angenommen, ich möchte nur die alternativen Elemente speichern (ca[0],c[2])?
– arunmoezhi
6. November 2012 um 19:18 Uhr
fragenmish
Verwenden Sie diese Funktion, um sie auszudrucken:
Notiz: Gießen der &var direkt zu einem int* oder uint16_t* würde auch MSVC funktionieren, aber dies verstößt gegen striktes Aliasing und ist ein undefiniertes Verhalten. Verwenden memcpy ist der standardkonforme Weg, dasselbe zu tun, und mit minimaler Optimierung generiert der Compiler genau denselben Binärcode.
Ersetzen llx mit lld wenn du int willst
– fragmisch
6. November 2012 um 18:52 Uhr
Es klappt. Ich habe uint32_t verwendet, um die 32-Bit-Ganzzahlen zu drucken. Aber die Ausgabe ist umgekehrt. Anstatt von 2,4,6,8 Ich bekomme 8,6,4,2. Tut _mm_add_epi32 Werte in umgekehrter Reihenfolge speichern?
– arunmoezhi
6. November 2012 um 19:00 Uhr
@NateEldredge: Wahrscheinlich nicht. EIN _mm_extract_epi32, oder in einem lokalen Array speichern sind normaler. Sie könnten auch a zuweisen union von a __m128i und ein Array. Dies ist in Ordnung für Test-/Debug-Drucke wenn Es funktioniert, wenn Sie es versuchen. Ein Debugger zeigt Ihnen jedoch einfacher als Debug-Ausdrucke, was sich in Ihren Vektoren befindet.
– Peter Cordes
17. April 2016 um 2:17 Uhr
Auch : __m128i bp = _mm_set_epi32(0xFF, 0xfe,0xfa,0xfb); std::cout << std::setfill('0') << std::hex<<std::setw(16)<< bp.m128i_i64[1]<<std::setw(16)<< bp.m128i_i64[0];
– Алексей Неудачин
25. Oktober 2018 um 14:06 Uhr
Wie wäre es mit int *val = (int*)&var? Dann bräuchte man die nicht memcpy.
– Nanashi No Gombe
15. Juni 2020 um 21:49 Uhr
Peter Kordes
Über gcc/clang/ICC/MSVC, C und C++ portierbar.
völlig sicher bei allen Optimierungsstufen: keine strikte Aliasing-Verletzung UB
print in hex als u8-, u16-, u32- oder u64-Elemente (basierend auf der Antwort von @ AG1)
Druckt in Speicherreihenfolge (das niederwertigste Element zuerst, z _mm_setr_epiX). Kehren Sie die Array-Indizes um, wenn Sie es vorziehen, in der gleichen Reihenfolge zu drucken, die Intels Handbücher verwenden, wobei sich das wichtigste Element auf der linken Seite befindet (wie z _mm_set_epiX). Verwandte: Konvention zum Anzeigen von Vektorregistern
Verwendung einer __m128i* aus einem Array von zu laden int ist sicher, weil die __m128 Typen sind so definiert, dass sie Aliasing erlauben, genau wie ISO C unsigned char*. (z. B. in den Kopfzeilen von gcc enthält die Definition __attribute__((may_alias)).)
Die Umkehrung ist nicht sicher (zeigt auf eine int* auf einen Teil von a __m128i Objekt). MSVC garantiert, dass dies sicher ist, aber GCC/clang nicht. (-fstrict-aliasing ist standardmäßig aktiviert). Es funktioniert manchmal mit GCC/clang, aber warum es riskieren? Es stört manchmal sogar die Optimierung; siehe diese Fragen und Antworten. Siehe auch Ist `reinterpret_cast`ing zwischen Hardware-SIMD-Vektorzeiger und dem entsprechenden Typ ein undefiniertes Verhalten?
Siehe GCC AVX _m256i Umwandlung in int Array führt zu falschen Werten für ein reales Beispiel für GCC-Breaking-Code, der auf zeigt int* an einer __m256i.
(uint32_t*) &my_vector verstößt gegen die C- und C++-Aliasing-Regeln und funktioniert nicht garantiert so, wie Sie es erwarten. Das Speichern in einem lokalen Array und der anschließende Zugriff darauf ist garantiert sicher. Es optimiert sogar mit den meisten Compilern, so dass Sie es bekommen movq / pextrq direkt von xmm zu Integer-Registern statt an tatsächlich z.B. speichern/neu laden.
Wenn Sie Portabilität auf C99 oder C++03 oder früher (dh ohne C11 / C++11) benötigen, entfernen Sie die alignas() und verwenden storeu Anstatt von store. Oder verwenden __attribute__((aligned(16))) oder __declspec( align(16) ) stattdessen.
(Wenn Sie Code mit Intrinsic schreiben, sollten Sie eine neuere Compiler-Version verwenden. Neuere Compiler machen normalerweise besser asm als ältere Compiler, einschließlich für SSE/AVX-Intrinsic. Aber vielleicht möchten Sie gcc-6.3 mit verwenden -std=gnu++03 C++03-Modus für eine Codebasis, die nicht für C++11 oder so bereit ist.)
Passen Sie die Formatzeichenfolgen an, wenn Sie für eine konsistente Ausgabebreite mit führenden Nullen auffüllen möchten. Sehen printf(3).
Antonio
Ich weiß, dass diese Frage mit C gekennzeichnet ist, aber es war das beste Suchergebnis, auch wenn nach einer C++-Lösung für dasselbe Problem gesucht wurde.
Dies könnte also eine C++-Implementierung sein:
#include <string>
#include <cstring>
#include <sstream>
#if defined(__SSE2__)
template <typename T>
std::string __m128i_toString(const __m128i var) {
std::stringstream sstr;
T values[16/sizeof(T)];
std::memcpy(values,&var,sizeof(values)); //See discussion below
if (sizeof(T) == 1) {
for (unsigned int i = 0; i < sizeof(__m128i); i++) { //C++11: Range for also possible
sstr << (int) values[i] << " ";
}
} else {
for (unsigned int i = 0; i < sizeof(__m128i) / sizeof(T); i++) { //C++11: Range for also possible
sstr << values[i] << " ";
}
}
return sstr.str();
}
#endif
Hinweis: Es gibt eine einfache Möglichkeit, dies zu vermeiden if (size(T)==1)siehe https://stackoverflow.com/a/28414758/2436175
Du solltest benutzen alignas(16) T values[16/sizeof(T)]; und _mm_storeu_si128( (__m128i*)values, var); Der Rest des Codes funktioniert dann einwandfrei. Und vereinfacht, weil man ein Range-for-like verwenden kann for(T v : values)Ich finde.
– Peter Cordes
15. Oktober 2017 um 7:13 Uhr
@PeterCordes Ich verstehe deinen Punkt. Ich frage mich, ob man stattdessen einfach ein memcpy verwenden könnte, das würde die Notwendigkeit ersparen, einen ausgerichteten Puffer zu benötigen.
– Antonio
15. Oktober 2017 um 22:19 Uhr
Siehe meine Antwort. Verwenden storeu Anstatt von store wenn Sie kein C++11 für haben alignas, oder Compiler-spezifische Anweisungen. Es wird wahrscheinlich noch wegoptimiert. (Und übrigens, modernes Windows / Linux richtet den Stapel bereits um 16B aus, sodass es den Compiler nichts kostet, den Puffer auszurichten, wenn er tatsächlich speichert / neu lädt.)
– Peter Cordes
15. Oktober 2017 um 22:24 Uhr
@PeterCordes Doch ist memcpy keine gültige Alternative?
– Antonio
16. Oktober 2017 um 2:02 Uhr
Ja, es ist nur ein Leistungsproblem, wenn Sie es mit einer Nicht-Potenz-von-2-Klasse verwenden, nicht uint*_t. Es ist sinnvoll, es aus Gründen der Lesbarkeit unverändert zu lassen. (Vor allem, da die Verwendung von std::string und einen String-Stream zum Drucken eines Vektors.) Wenn Sie dies in eine Bibliothek stellen würden, damit die Leute es verwenden können, ohne es anzusehen, anstatt einer SO-Antwort, würden Sie andere Entscheidungen treffen.
– Peter Cordes
17. Oktober 2017 um 8:00 Uhr
Lucien
#include<stdio.h>
#include<emmintrin.h>
int main()
{
__m128i a = _mm_set_epi32(1,2,3,4);
__m128i b = _mm_set_epi32(1,2,3,4);
__m128i c;
const int32_t* q;
//add a pointer
c = _mm_add_epi32(a,b);
q = (const int32_t*) &c;
printf("%d\n",q[2]);
//printf("%d\n",c[2]);
return 0;
}
Versuchen Sie diesen Code.
13716600cookie-checkDrucken Sie eine __m128i-Variableyes
Beachte das auch
__m128i
hat keine Informationen über den Typ, der gespeichert wird. Es können 8-Bit-Ints, 16-Bit-Ints, 32-Bit usw. sein. Einige Compiler unterstützen die.m128i_i32
Felderweiterungen. Aber es ist definitiv kein Standard und nicht in GCC.– Mystisch
6. November 2012 um 18:59 Uhr
im Zusammenhang mit dem Titel: Wie drucke ich die __uint128_t-Nummer mit gcc?
– jfs
6. November 2012 um 19:00 Uhr
Beachten Sie, dass einige Compiler eine eingebaute printf-Unterstützung für SIMD-Typen haben, z. B. Apples Versionen von gcc, clang usw., die alle unterstützt werden
%vld
zum Drucken u__m128i
als 4 x 32 bit ints.– PaulR
6. November 2012 um 19:12 Uhr
Ich verwende den Intel-Compiler
– arunmoezhi
6. November 2012 um 19:15 Uhr
Gibt es eine Möglichkeit, eine maskierte Addition durchzuführen? Angenommen, ich möchte nur die alternativen Elemente speichern (ca[0],c[2])?
– arunmoezhi
6. November 2012 um 19:18 Uhr