在我的代碼中,我必須處理webmasocket數據包的「unmasking」,這實質上意味着XOR'ing任意長度的未對齊數據。感謝SO(Websocket data unmasking/multi byte xor),我已經發現瞭如何(希望)使用SSE2/AVX2擴展來加快速度,但現在看,我認爲我對未對齊數據的處理是完全不理想的。有沒有什麼辦法來優化我的代碼,或者至少讓它更簡單一些,或者我的代碼已經是最好的了?優化未對齊的SSE2/AVX2 XOR
下面是代碼的重要部分(對於這個問題,我假設數據將永遠足以運行一次AVX2循環,但同時它最多隻能運行幾次) :
// 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:請忽略使用#ifdef而不是cpuid或cpuid等來檢測cpu標誌。
您是否試過計時代碼? (另外,你可能想用圓括號將你的條件語句中的'&'換行) –
定時不會真的有幫助,因爲我只能對輸入的數據進行假設,但不會得到任何實際的結果投入幾個月後。此外,我只會得到一些絕對數字,這對我來說並沒有什麼幫助,因爲我的問題沒有找到代碼用xy輸入執行需要多長時間,但是如何使它更快,例如,我不知道要改變什麼。 P.S .:裹住按鈕&爲了更容易理解,thx的提示! – griffin
我認爲你會發現數據依賴性失速超過了對齊/未對齊的好處。如果您可以將循環展開2倍,則應該看到顯着的改進。 – BitBank