2017-01-27 28 views
0

我目前正試圖實現我的一些現有的標量代碼的AVX2版本(Haswell CPU)。它實現這樣的步驟:AVX2聚集加載兩個整數的結構

struct entry { 
    uint32_t low, high; 
}; 

// both filled with "random" data in previous loops 
std::vector<entry> table; 
std::vector<int> queue; // this is strictly increasing but 
          // without a constant delta 

for (auto index : queue) { 
    auto v = table[index]; 
    uint32_t rank = v.high + __builtin_popcount(_bzhi_u32(v.low, index % 32)); 
    use_rank(rank); // contains a lot of integer operations which nicely map to avx2 
} 

我實現了這個與2聚集指令,每個負載這樣的INT32:

__m256iv_low = _mm256_i32gather_epi32 (reinterpret_cast<int *>(table.data()) + 0, index, 8); 
__m256i v_high = _mm256_i32gather_epi32 (reinterpret_cast<int *>(table.data()) + 1, index, 8); 

有一個更快的方法兩個加載這些值?我曾考慮過使用2 64位加載(只發出讀取數量的一半=執行端口的流量更少),然後將結果向量洗牌以獲得v_low和v_high,但令人傷心的是,據我所知,大部分洗牌函數只允許分別對128位進行混洗。

編輯保羅R: 此代碼是使用我的壓縮算法中使用的Burrows惠勒變換的子字符串枚舉例程的一部分。 table包含位矢量上的等級數據。高部分包含先前條目中的數目,下部分被屏蔽掉並被彈出,然後被添加以獲得給定索引前面的最終設置位數。之後,更多的計算髮生,幸運地是很好的並行化。

隊列中的增量在開始和結束時都非常高(由於算法的性質)。這導致了很多緩存未命中的情況,這就是爲什麼我使用轉換從SoA切換到AoS的原因,以減少標量代碼中加載端口的壓力。

使用SoA也會導致相同的獨立採集指令,但會使訪問的緩存行數量加倍。

編輯(部分回答): 我試圖使用兩個_mm_i32gather_epi64到存儲器一半訪問的次數(以及因此週期,見here)。

__m256i index; // contains the indices 
__m128i low = _mm256_extractf128_si256(index, 0); 
__m128i high = _mm256_extractf128_si256(index, 1); 
__m256i v_part1 = _mm256_i32gather_epi64(reinterpret_cast<long long int*>(table.data()), low , 8); 
__m256i v_part2 = _mm256_i32gather_epi64(reinterpret_cast<long long int*>(table.data()), high, 8); 
它加載我的數據分爲兩個YMM

註冊該格式(沒有C++):

register v_part1: 
[v[0].low][v[0].high][v[1].low][v[1].high][v[2].low][v[2].high][v[3].low][v[3].high] 
register v_part2: 
[v[4].low][v[4].high][v[5].low][v[5].high][v[6].low][v[6].high][v[7].low][v[7].high] 

是否有交錯他們,以獲得原始格式的有效途徑:

register v_low: 
[v[0].low][v[1].low][v[2].low][v[3].low][v[4].low][v[5].low][v[6].low][v[7].low] 
register v_high: 
[v[0].high][v[1].high][v[2].high][v[3].high][v[4].high][v[5].high][v[6].high][v[7].high] 
+1

該代碼無意義且無效C++。 –

+2

@JohnZwinck:它是AVX內在函數。 – MSalters

+0

@ MSalters:我指的是像'uint64_t v; v.low'。 –

回答

1

我發現了一種使用5條指令自己重新排列值的方法:

// this results in [01][45][23][67] when gathering 
index = _mm256_permute4x64_epi64(index, _MM_SHUFFLE(3,1,2,0)); 

// gather the values 
__m256i v_part1 = _mm256_i32gather_epi64(i, _mm256_extractf128_si256(index, 0), 8); 
__m256i v_part2 = _mm256_i32gather_epi64(i, _mm256_extractf128_si256(index, 1), 8); 

// seperates low and high values 
v_part1 = _mm256_shuffle_epi32(v_part1, _MM_SHUFFLE(3,1,2,0)); 
v_part2 = _mm256_shuffle_epi32(v_part2, _MM_SHUFFLE(3,1,2,0)); 

// unpack merges lows and highs: [01][23][45][56] 
o1 = _mm256_unpackhi_epi64(v_part1, v_part2); 
o2 = _mm256_unpacklo_epi64(v_part1, v_part2);