In meinem Code muss ich "Demaskierung" von Websocket-Paketen behandeln, was im Wesentlichen XOR'ing nicht ausgerichtete Daten beliebiger Länge bedeutet. Dank SO (Websocket data unmasking/multi byte xor) habe ich bereits herausgefunden, wie man dies (hoffentlich) mit SSE2/AVX2-Erweiterungen beschleunigen kann, aber wenn ich es jetzt betrachte, scheint mir mein Umgang mit nicht ausgerichteten Daten absolut nicht optimal zu sein. Gibt es eine Möglichkeit, meinen Code zu optimieren oder ihn bei gleicher Leistung zumindest einfacher zu machen, oder ist mein Code bereits der beste?optimieren unausgerichtete SSE2/AVX2 XOR
Hier ist der wichtige Teil des Codes (für die Frage nehme ich an, dass Daten immer mindestens genug sein werden, um den AVX2-Zyklus einmal auszuführen, aber zur gleichen Zeit wird es meistens nur ein paar Mal laufen) :
// circular shift left for uint32
int cshiftl_u32(uint32_t num, uint8_t shift) {
return (num << shift) | (num >> (32 - shift));
}
// circular shift right for uint32
int cshiftr_u32(uint32_t num, uint8_t shift) {
return (num >> shift) | (num << (32 - shift));
}
void optimized_xor_32(uint32_t mask, uint8_t *ds, uint8_t *de) {
if (ds == de) return; // zero data len -> nothing to do
uint8_t maskOffset = 0;
// process single bytes till 4 byte alignment (<= 3)
for (; ds < de && ((uint64_t)ds & (uint64_t)3); ds++) {
*ds ^= *((uint8_t *)(&mask) + maskOffset);
maskOffset = (maskOffset + 1) & (uint8_t)3;
}
if (ds == de) return; // done, return
if (maskOffset != 0) { // circular left-shift mask around so it works for other instructions
mask = cshiftl_u32(mask, maskOffset);
maskOffset = 0;
}
// process 4 byte block till 8 byte alignment (<= 1)
uint8_t *de32 = (uint8_t *)((uint64_t)de & ~((uint64_t)31));
if (ds < de32 && ((uint64_t)de & (uint64_t)7)) {
*(uint32_t *)ds ^= mask; // mask is uint32_t
if (++ds == de) return;
}
// process 8 byte block till 16 byte alignment (<= 1)
uint64_t mask64 = mask | (mask << 4);
uint8_t *de64 = (uint8_t *)((uint64_t)de & ~((uint64_t)63));
if (ds < de64 && ((uint64_t)ds & (uint64_t)15)) {
*(uint64_t *)ds ^= mask64;
if (++ds == de) return; // done, return
}
// process 16 byte block till 32 byte alignment (<= 1) (if supported)
#ifdef CPU_SSE2
__m128i v128, v128_mask;
v128_mask = _mm_set1_epi32(mask);
uint8_t *de128 = (uint8_t *)((uint64_t)de & ~((uint64_t)127));
if (ds < de128 && ((uint64_t)ds & (uint64_t)31)) {
v128 = _mm_load_si128((__m128i *)ds);
v128 = _mm_xor_si128(v128, v128_mask);
_mm_store_si128((__m128i *)ds, v128);
if (++ds == de) return; // done, return
}
#endif
#ifdef CPU_AVX2 // process 32 byte blocks (if supported -> haswell upwards)
__m256i v256, v256_mask;
v256_mask = _mm256_set1_epi32(mask);
uint8_t *de256 = (uint8_t *)((uint64_t)de & ~((uint64_t)255));
for (; ds < de256; ds+=32) {
v256 = _mm256_load_si256((__m256i *)ds);
v256 = _mm256_xor_si256(v256, v256_mask);
_mm256_store_si256((__m256i *)ds, v256);
}
if (ds == de) return; // done, return
#endif
#ifdef CPU_SSE2 // process remaining 16 byte blocks (if supported)
for (; ds < de128; ds+=16) {
v128 = _mm_load_si128((__m128i *)ds);
v128 = _mm_xor_si128(v128, v128_mask);
_mm_store_si128((__m128i *)ds, v128);
}
if (ds == de) return; // done, return
#endif
// process remaining 8 byte blocks
// this should always be supported, so remaining can be assumed to be executed <= 1 times
for (; ds < de64; ds += 8) {
*(uint64_t *)ds ^= mask64;
}
if (ds == de) return; // done, return
// process remaining 4 byte blocks (<= 1)
if (ds < de32) {
*(uint32_t *)ds ^= mask;
if (++ds == de) return; // done, return
}
// process remaining bytes (<= 3)
for (; ds < de; ds ++) {
*ds ^= *((uint8_t *)(&mask) + maskOffset);
maskOffset = (maskOffset + 1) & (uint8_t)3;
}
}
PS: die Verwendung von #ifdef statt cpuid oder dergleichen für die CPU-Markierungserkennung Bitte ignorieren.
Haben Sie versucht, Ihren Code zu takten? (Sie könnten auch das bitweise '&' in Ihren Bedingungen mit runden Klammern umschließen) –
Timing würde nicht wirklich helfen, da ich nur Annahmen über die Daten machen kann, die ich als Eingabe erhalten werde, aber keine echten erhalten werde Eingang für ein paar Monate zu kommen. Außerdem würde ich nur eine absolute Zahl mit dem Timing bekommen, was mir nicht wirklich hilft, da mein Problem nicht herauszufinden ist, wie lange dieser Code benötigt, um mit xy-Eingabe auszuführen, sondern wie man es schneller macht, z. Ich habe keine Vorstellung davon, was ich ändern soll. S.S .: Bitweise eingepackt & zum leichteren Verständnis, danke für den Hinweis! – griffin
Ich denke, Sie werden feststellen, dass die Datenabhängigkeit den Nutzen von Aligned/Unaligned überwiegt. Wenn Sie Ihre Schleifen 2x ausrollen können, sollten Sie eine deutliche Verbesserung feststellen. – BitBank