2017-08-07 47 views
4

比方說,例如,我有2個變量__m256irowscols,在他們裏面的值是:現在從`__m256i`使用值來訪問有效的陣列 - SIMD

rows: 0, 2, 7, 5, 7, 2, 3, 0 
cols: 1, 2, 7, 5, 7, 2, 2, 6 

,這些值表示爲8個點的xy位置,從而,在這種情況下,我將有以下幾點:

p0: [0, 1], p1: [2, 2], p2: [7, 7], p3: [5, 5] 
p4: [7, 7], p5: [2, 2], p6: [3, 2], p7: [0, 6] 

我也有一個陣列稱爲lut將具有的值int類型:

lut: [0, 1, 2, 3, ..., 60, 61, 62, 63] 

我想要做的,是從rowscols變量使用這些位置值,用它訪問lut陣列並創建一個新__m256i值與lut訪問值。

我知道該怎麼做,這將是存儲rowscols值大小爲8兩個int陣列,然後在同一時間從lut陣列一個讀出的值,然後使用_mm256_set_epi32()創建新_m256i價值的方式。

這有效,但在我看來是非常低效的..所以我的問題是如果有辦法做得更快。

請注意,這些值僅僅是一個更具體的例子,並lut並不需要有有序值或大小64

的感謝!

+3

查找表通常在現代處理器上效率不高,因爲它們總是創建數據依賴關係,所以如果您可以找到一種方法來用某種算術公式替換查找表,即使它有點複雜,它可以更快。否則,請參閱chtz的答案。 –

+0

向我們顯示您的代碼。比它可能給你一個優化提示。 – xMRi

+1

有時你可以將你的索引切分成4位的塊,並用'pshufb'來獲得一個16元的LUT。 (例如看看http://wm.ite.pl/articles/sse-popcount.html的SSE/AVX popcount實現。)但是當你的索引不是「可分離的」時,你必須用低字節進行多次混洗,並混合高字節,所以這是O(2 ^(n-4))適用於n位有效位,其中n> = 4。 –

回答

5

您可以構建使用avx2 gather instruction的解決方案,像這樣

// index = (rows << 3) + cols; 
const __m256i index = _mm256_add_epi32(_mm256_slli_epi32(rows, 3), cols); 
// result = lut[index]; 
const __m256i result = _mm256_i32gather_epi32(lut, index, 4); 

要知道,目前CPU的收集指令有相當龐大的延遲,所以除非你可以實際使用result之前交錯一些指令,這可能不是值得使用。

爲了解釋的4因素:scale因子在

__m256i _mm256_i32gather_epi32 (int const* base_addr, __m256i vindex, const int scale) 

被認爲是實際的字節偏移量,即,用於每個索引的返回的值是:

*(const int*)((const char*) base_addr + scale*index) 

我不要知道這種行爲是否有很多用例(也許這是爲了能夠使用1字節或2字節的條目訪問LUT(以後需要一些掩碼))。也許這只是允許的,因爲可以縮放4,而縮放1/4或1/2不會(如果有人真的需要這個)。

+0

謝謝,這正是我想要的!只有一件事,我不明白爲什麼在這種情況下比例是4。你能向我解釋爲什麼? –

+0

@ E.B。我添加了一個解釋(雖然我不確定該行爲的實際動機) – chtz

+1

解壓縮/解碼函數可能在向量中具有字節偏移量,而不是C樣式的非縮放索引。我認爲他們只是決定給你增加1,2,4或8倍的靈活性,因爲他們在指令編碼中有2位,它們可能用於通常的目的。譯碼一個VSIB字節與解碼器(我假設爲內部uop格式)幾乎相同,是一個常規的SIB字節(用於常規非收集索引尋址模式,如'mov eax,[rdi + rcx * 4 + 1234] )。 –