很明顯的,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