Monday, June 14, 2010

AT&Tx86ASM lock前綴 更快的SpinLock

為了要在GCC上編譯我的Program,Program中的內嵌ASM必須改用AT&T規範的ASM來寫

很明顯的,AT&T ASM與Intel ASM有非常大的不同,暫存器前必須加上%前綴很快的就像Python判斷式不需要加( )成為我的噩夢

在克服%前綴問題後,開始實作SpinLock

以指令cmpxchg代替了Windows API的InterlockedCompareExchange,它的特色我在前面的文章InterlockedCompareExchange 鎖上 比較 交換有提過了


就在我將lock前綴(讓CPU存取同步)加到cmpxchg時,一個麻煩的錯誤發生了Invalid lock sequence,MSDN的解釋同樣令人困惑不解(對這我並不感到意外,這種事情常發生)。
就在嘗試各種方法來解決這個不了解的錯誤時,發現到VC++同樣ASM(lock cmpxchg),竟然沒有問題。於是我開始比對VC++跟GCC編譯出來到底有何不同。很快的,發現lock cmpxchg中的目標數不能為暫存器,具體原因還不清楚,目前確定直接傳入記憶體位址是個不錯的主意。


我會遇上這個問題的原因就更加曲折了,來自於對AT&T ASM的不了解。
AT&T ASM在傳入外部C變數時(ASM內嵌在C Program中),必須透過一個input operands來告訴編譯器傳入哪些變數,並且可指定編譯器要如何傳入。
不幸的是,我將它指定為"以暫存器形式傳入",並且將它傳到lock cmpxchg的目標數,錯誤就因此發生。解決辦法只要將它指定為"以記憶體位址形式傳入",就能簡單的解決此問題。


為了解決這個錯誤,我花了一個小時的時間,並更加了解AT&T ASM
實作SpinLock後,測試速度時又給了我另一個驚喜
它比之前透過InterlockedCompareExchange的版本快了大概30%,更加印證了之前的這篇文章Windows API Mutex 與 Yourself SpinLock所說的,透過自己實作有時會比呼叫Windows API來的快。


主要的等待並取得Lock代碼

typedef struct _lock{
    unsigned long lock;
}lock;





static void WaitLock(void* pLock){
    lock* pWaitlock;

    pWaitlock=(lock*)pLock;
    __asm__ __volatile__("WaitLoop:;"
                         "movl $0,%%eax;"
                         "movl $1,%%edx;"
                         "lock;cmpxchgl %%edx,%0;"
                         "jz WaitRet;"
                         "rep;nop;"
                         "jnz WaitLoop;"
                         "WaitRet:;"
                         :
                         :"m"(pWaitlock->lock)
                         :"eax","edx","memory");

    return;
}

No comments:

Post a Comment