2016-01-22 49 views
3

下面的代碼在爲一個至少phi編譯時會拋出 Error: cmovc is not supported on k1omx86中的原子測試和設置:內聯asm或編譯器生成的鎖定bts?

但它確實編譯正常的Xeon處理器。

#include<stdio.h> 
int main() 
{ 
    int in=5; 
    int bit=1; 
    int x=0, y=1; 
    int& inRef = in; 
    printf("in=%d\n",in); 
    asm("lock bts %2,%0\ncmovc %3,%1" : "+m" (inRef), "+r"(y) : "r" (bit), "r"(x)); 
    printf("in=%d\n",in); 
} 

編譯器 - icc (ICC) 13.1.0 20130121

相關問題:bit test and set (BTS) on a tbb atomic variable

+1

IIRC,第一代Xeon Phi基於P5內核(Pentium和Pentium MMX)。直到P6(又名Pentium Pro)才引入'cmov'。所以我認爲這很正常。讓編譯器通過編寫一個正常的三元運算符來完成它的工作。另請注意,帶有內存操作數的「bts」超慢。不要這樣做。另外,我認爲你正在測試'in'地址中的位,因爲你要求'&in'存儲在內存中。那是你想要的嗎? –

+1

你的問題是什麼? –

+0

我認爲Xeon phi也是基於x86的,它應該可以正常工作 – arunmoezhi

回答

3

IIRC,第一代的Xeon Phi是基於P5核(Pentium和奔騰MMX)。直到P6(aka Pentium Pro)才引入cmov。所以我認爲這很正常。

讓編譯器通過編寫一個正常的三元運算符來完成它的工作。

其次,cmov是一個比setc更差的選擇,因爲你想根據進位標誌產生一個0或1。請參閱下面的我的代碼。

另請注意,帶內存操作數的bts超慢,因此您不希望它生成該代碼,尤其是,在將x86指令解碼爲uops的CPU上(如現代Xeon)。根據http://agner.org/optimize/,即使在P5上,bts m, r也比bts m, i慢得多,所以不要這樣做。

只需要編譯器的in在寄存器中,或者更好的是,不要使用內聯asm。


由於OP顯然希望這原子的工作,最好的解決辦法是使用C++ 11的std::atomic::fetch_or,並把它留給編譯器生成lock bts

std::atomic_flag有一個test_and_set函數,但是IDK是否有辦法將它們緊緊地打包。也許作爲結構中的位域?雖然不太可能。我也沒有看到std::bitset的原子操作。

不幸的是,gcc和鏗鏘的當前版本不會從fetch_or產生lock bts,即使在備受更快的即時操作數形式是可用的。我想出了下述(godbolt link):

#include <atomic> 
#include <stdio.h> 

// wastes instructions when the return value isn't used. 
// gcc 6.0 has syntax for using flags as output operands 

// IDK if lock BTS is better than lock cmpxchg. 
// However, gcc doesn't use lock BTS even with -Os 
int atomic_bts_asm(std::atomic<unsigned> *x, int bit) { 
    int retval = 0; // the compiler still provides a zeroed reg as input even if retval isn't used after the asm :/ 
    // Letting the compiler do the xor means we can use a m constraint, in case this is inlined where we're storing to already zeroed memory 
    // It unfortunately doesn't help for overwriting a value that's already known to be 0 or 1. 
    asm(// "xor  %[rv], %[rv]\n\t" 
     "lock bts %[bit], %[x]\n\t" 
     "setc  %b[rv]\n\t" // hope that the compiler zeroed with xor to avoid a partial-register stall 
     : [x] "+m" (*x), [rv] "+rm"(retval) 
     : [bit] "ri" (bit)); 
    return retval; 
} 

// save an insn when retval isn't used, but still doesn't avoid the setc 
// leads to the less-efficient setc/ movzbl sequence when the result is needed :/ 
int atomic_bts_asm2(std::atomic<unsigned> *x, int bit) { 
    uint8_t retval; 
    asm("lock bts %[bit], %[x]\n\t" 
     "setc  %b[rv]\n\t" 
     : [x] "+m" (*x), [rv] "=rm"(retval) 
     : [bit] "ri" (bit)); 
    return retval; 
} 


int atomic_bts(std::atomic<unsigned> *x, unsigned int bit) { 
    // bit &= 31; // stops gcc from using shlx? 
    unsigned bitmask = 1<<bit; 
    //int oldval = x->fetch_or(bitmask, std::memory_order_relaxed); 

    int oldval = x->fetch_or(bitmask, std::memory_order_acq_rel); 
    // acquire and release semantics are free on x86 
    // Also, any atomic rmw needs a lock prefix, which is a full memory barrier (seq_cst) anyway. 

    if (oldval & bitmask) 
    return 1; 
    else 
    return 0; 
} 

如圖What is the best way to set a register to zero in x86 assembly: xor, mov or and?xor /設定標誌討論/ setc是當需要將其結果作爲0或1的值用於所有現代CPU的最佳序列。我沒有真正考慮過P5,但setcc在P5上很快,所以應該沒問題。

當然,如果你想分支而不是存儲它,inline asm和C之間的邊界是一個障礙。花費兩條指令來存儲0或1,僅用於測試/分支,將是非常愚蠢的。

如果是選項,gcc6的標誌操作數語法當然值得查看。 (如果你需要一個編譯器來支持英特爾MIC,則可能不需要)。

+0

'bts m,r'和'bts m,i'之間的區別是什麼? i''表示立即尋址模式? – arunmoezhi

+0

@arunmoezhi:是的,'i'是立即數,如果位置是一個編譯時常量,那麼你會得到更好的結果,因爲CPU知道最終的內存地址儘管如此,它可以解碼更少的uops,例如使用'ri'約束,儘管如此,我並沒有太多的運氣讓gcc生成'lock bts'。 http://goo.gl/yKYTfY http://en.cppreference.com/w/cpp/atomic/atomic_flag看起來很有用(有一個原子test_and_set函數),但o對於獨立標誌,不適用於int中的位:/ –

+0

謝謝。在'atomic_bts'函數中,彙編代碼有'cmpxchg'。但在多線程環境中,compare_and_exchange可能比'bts'慢。例如,兩個線程「A」和「B」分別嘗試「同時」設置位「bitA」和「bitB」。如果我使用'cmpxchg',那麼只有一個會成功。但是這兩個位上的「bts」將會成功。 – arunmoezhi

相關問題