2012-02-19 83 views
7

我需要一個具有周期2^128的__m128i變量的函數。它不需要單調增加(就像一個計數器),而是每訪問一次值。128位SSE計數器?

我能想到的最簡單的例子實際上是一個128位的計數器,但我發現很難在SSE中實現。有沒有更簡單/更快的解決方案?

+2

爲什麼你需要訪問2^128值?地球上的任何一臺計算機都無法做到這一點。你不能使用64位int嗎? – usr 2012-10-31 12:03:33

+0

在時鐘速度爲千兆赫茲的處理器上達成一致,在64位計數器耗盡之前,您可以在大約584年的每個週期內消耗一個數字。 – Damon 2014-04-02 11:48:53

回答

5

這是一個單調的計數器。我不確定你是否可以簡單地調用它。

假設ONEZERO總是在寄存器中,那麼這應該編譯成5條指令。 (7或8,如果不使用VEX編碼)

inline __m128i nextc(__m128i x){ 
    const __m128i ONE = _mm_setr_epi32(1,0,0,0); 
    const __m128i ZERO = _mm_setzero_si128(); 

    x = _mm_add_epi64(x,ONE); 
    __m128i t = _mm_cmpeq_epi64(x,ZERO); 
    t = _mm_and_si128(t,ONE); 
    t = _mm_unpacklo_epi64(ZERO,t); 
    x = _mm_add_epi64(x,t); 

    return x; 
} 

試驗規程(MSVC):

int main() { 

    __m128i x = _mm_setr_epi32(0xfffffffa,0xffffffff,1,0); 

    int c = 0; 
    while (c++ < 10){ 
     cout << x.m128i_u64[0] << " " << x.m128i_u64[1] << endl; 
     x = nextc(x); 
    } 

    return 0; 
} 

輸出:

18446744073709551610 1 
18446744073709551611 1 
18446744073709551612 1 
18446744073709551613 1 
18446744073709551614 1 
18446744073709551615 1 
0 2 
1 2 
2 2 
3 2 

稍好版本所建議@ Norbert P.它比我的原始解決方案節省了1條指令。

inline __m128i nextc(__m128i x){ 
    const __m128i ONE = _mm_setr_epi32(1,0,0,0); 
    const __m128i ZERO = _mm_setzero_si128(); 

    x = _mm_add_epi64(x,ONE); 
    __m128i t = _mm_cmpeq_epi64(x,ZERO); 
    t = _mm_unpacklo_epi64(ZERO,t); 
    x = _mm_sub_epi64(x,t); 

    return x; 
} 
+0

謝謝,你的代碼比我的要乾淨得多。我會稍微等一下,看看有沒有其他解決方案的解決方案。 – jk4736 2012-02-19 18:47:58

+0

問題是,如果這真的比不使用任何SSE的解決方案更快。我的意思是使用2個64位無符號結構和1個分支的明顯解決方案將避免可能的SSE開銷,並且分支將是非常可預測的。 – Voo 2012-02-19 23:37:32

+0

@Voo它可能取決於需要什麼樣的價值形式。如果在通用寄存器或內存中需要它,那麼'add + adc'將會是最快的。如果在SSE寄存器中需要它,那麼5個SSE-Int指令可能會比任何提取/插入更快。由於您使用不同的字大小訪問相同的內存,因此通過結構/聯合將其傳遞到內存中可能會使加載/存儲緩衝區停頓。 – Mysticial 2012-02-19 23:44:20

4

永遠不要忘記KISS原則。

粘貼(需要無符號整數C標準環繞,因此訪問每個值僅一次)這樣的:(針對x64)

__uint128_t inc(__uint128_t x) { 
    return x+1; 
} 

this收率:

addq $1, %rdi 
    adcq $0, %rsi 
    movq %rdi, %rax 
    movq %rsi, %rdx 
    ret 

容易/夠快?如果內聯,你可能能夠與剛剛addq/adcq脫身(在movq S和由64位ABI需要ret:如果內聯函數,不需要他們)


爲了解決VOO的評論關於MSVC的suckiness,您可以使用以下命令:

inline void inc(unsigned long long *x, unsigned long long *y) { 
    if (!++*x) ++*y; // yay for obfuscation! 
} 

我沒有MSVC安裝在附近,所以我不能測試,但它應該類似產量東西我上面發佈的內容。那麼,如果你真的需要一個__m128i,你應該可以把cast兩半。

+0

這個解決方案的問題是從SSE寄存器加載和存儲將需要很長時間。 – jk4736 2012-02-19 18:46:27

+0

如果該代碼是有效的,則取決於編譯器。很確定它不在MSVC下 - __m128只是一個較小基元聯合的結構。 – Voo 2012-02-19 23:18:23

+0

@ jk4736我的代碼片段中的所有寄存器都是非SSE 64位的... – CAFxX 2012-02-20 10:54:29