2016-07-05 47 views
2

通過GCC文檔:x86-transactional-memory-intrinsics.html,當交易失敗/中止,_xbegin()應返回中止狀態。但是,我發現它有時會返回0。而且頻率非常高。 ** _ xbegin()**會返回0的是什麼樣的情況?硬件事務內存:_xbegin()返回0

查看手冊後,我發現很多情況可能會導致這種結果。例如,CPUID,SYSTEMCALL,CFLUSH.etc。但是,我不認爲我的代碼觸發了其中的任何一個。

這是我的代碼:模擬一個小銀行,隨機轉賬1 $到另一個賬戶。

#include "immintrin.h" 
#include <thread> 
#include <unistd.h> 
#include <iostream> 

using namespace std; 

#define n_threads 1 
#define OPSIZE 1000000000 
typedef struct Account{ 
    long balance; 
    long number; 
} __attribute__((aligned(64))) account_t; 

typedef struct Bank{ 
    account_t* accounts; 
    long size; 
} bank_t; 

bool done = 0; 
long *tx, *_abort, *capacity, *debug, *failed, *conflict, *zero; 

void* f1(bank_t* bank, int id){ 
    for(int i=0; i<OPSIZE; i++){ 
     int src = rand()%bank->size; 
     int dst = rand()%bank->size; 
     while(src == dst){ 
      dst = rand()%bank->size; 
     } 

     while(true){ 
      unsigned stat = _xbegin(); 
      if(stat == _XBEGIN_STARTED){ 
       bank->accounts[src].balance++; 
       bank->accounts[dst].balance--; 
       _xend(); 
       asm volatile("":::"memory");  
       tx[id]++; 
       break; 
      }else{ 
       _abort[id]++; 

       if (stat == 0){ 
        zero[id]++; 
       } 
       if (stat & _XABORT_CONFLICT){ 
        conflict[id]++; 
       } 
       if (stat & _XABORT_CAPACITY){ 
        capacity[id]++; 
       } 
       if (stat & _XABORT_DEBUG){ 
        debug[id]++; 
       } 
       if ((stat & _XABORT_RETRY) == 0){ 
        failed[id]++; 
        break; 
       } 
       if (stat & _XABORT_NESTED){ 
        printf("[ PANIC ] _XABORT_NESTED\n"); 
        exit(-1); 
       } 
       if (stat & _XABORT_EXPLICIT){ 
        printf("[ panic ] _XBEGIN_EXPLICIT\n"); 
        exit(-1); 
       } 
      } 
     } 
    } 
    return NULL; 
} 
void* f2(bank_t* bank){ 
    printf("_heartbeat function\n"); 
    long last_txs=0, last_aborts=0, last_capacities=0, last_debugs=0, last_faileds=0, last_conflicts=0, last_zeros = 0; 
    long txs=0, aborts=0, capacities=0, debugs=0, faileds=0, conflicts=0, zeros = 0; 
    while(1){ 
     last_txs = txs; 
     last_aborts = aborts; 
     last_capacities = capacities; 
     last_debugs = debugs; 
     last_conflicts = conflicts; 
     last_faileds = faileds; 
     last_zeros = zeros; 

     txs=aborts=capacities=debugs=faileds=conflicts=zeros = 0; 
     for(int i=0; i<n_threads; i++){ 
      txs += tx[i]; 
      aborts += _abort[i]; 
      faileds += failed[i]; 
      capacities += capacity[i]; 
      debugs += debug[i]; 
      conflicts += conflict[i]; 
      zeros += zero[i]; 
     } 

     printf("txs\t%ld\taborts\t\t%ld\tfaileds\t%ld\tcapacities\t%ld\tdebugs\t%ld\tconflit\t%ld\tzero\t%ld\n", 
      txs - last_txs, aborts - last_aborts , faileds - last_faileds, 
      capacities- last_capacities, debugs - last_debugs, conflicts - last_conflicts, 
      zeros- last_zeros); 

     sleep(1); 
    } 
} 

int main(int argc, char** argv){ 
    int accounts = 10240; 

    bank_t* bank = new bank_t; 
    bank->accounts = new account_t[accounts]; 
    bank->size = accounts; 

    for(int i=0; i<accounts; i++){ 
     bank->accounts[i].number = i; 
     bank->accounts[i].balance = 0; 
    } 

    thread* pid[n_threads]; 
    tx = new long[n_threads]; 
    _abort = new long[n_threads]; 
    capacity = new long[n_threads]; 
    debug = new long[n_threads]; 
    failed = new long[n_threads]; 
    conflict = new long[n_threads]; 
    zero = new long[n_threads]; 

    thread* _heartbeat = new thread(f2, bank); 
    for(int i=0; i<n_threads; i++){ 
     tx[i] = _abort[i] = capacity[i] = debug[i] = failed[i] = conflict[i] = zero[i] = 0; 
     pid[i] = new thread(f1, bank, i); 
    } 

// sleep(5); 
    for(int i=0; i<n_threads;i++){ 
     pid[i]->join(); 
    } 
    return 0; 
} 

補充:

  1. 所有賬戶的64位對齊。我打印銀行 - >賬戶[0],銀行 - >賬戶1地址。 0xf41080,0xf410c0。
  2. 使用-O0和asm volatile("":::"memory");因此沒有指令重新排序的問題。
  3. 中止率在時間上升。這裏是結果

    txs  84  aborts   0  faileds 0  capacities  0  debugs 0  conflit 0  zero 0 
    txs  17070804  aborts   71  faileds 68  capacities  9  debugs 0  conflit 3  zero 59 
    txs  58838   aborts   9516662 faileds 9516661 capacities  0  debugs 0  conflit 1  zero 9516661 
    txs  0    aborts   9550428 faileds 9550428 capacities  0  debugs 0  conflit 0  zero 9550428 
    txs  0    aborts   9549254 faileds 9549254 capacities  0  debugs 0  conflit 0  zero 9549254 
    
  4. 即使通過n_th​​reads是1,結果也是一樣的。

  5. 如果我在後退後添加粗體鎖,則結果看起來是正確的。

    int fallback_lock; 
    
    bool 
    rtm_begin(int id) 
    { 
        while(true) { 
         unsigned stat; 
         stat = _xbegin(); 
         if(stat == _XBEGIN_STARTED) { 
          return true; 
         } else { 
          _abort[id]++; 
          if (stat == 0){ 
           zero[id]++; 
          } 
          //call some fallback function 
          if (stat& _XABORT_CONFLICT){ 
           conflict[id]++; 
          } 
    
          //will not succeed on a retry 
          if ((stat & _XABORT_RETRY) == 0) { 
           failed[id]++; 
           //grab a fallback lock 
           while (!__sync_bool_compare_and_swap(&fallback_lock,0,1)) { 
           } 
           return false; 
          } 
         } 
        } 
    } 
    .... 
    
    in_rtm = rtm_begin(id); 
    y = fallback_lock; 
    accounts[src].balance--; 
    accounts[dst].balance++; 
    if (in_rtm){ 
        _xend(); 
    }else{ 
        while(!__sync_bool_compare_and_swap(&fallback_lock, 1, 0)){ 
        } 
    } 
    
+0

嗯。 Cursory檢查表明事情沒有問題(儘管我沒有運行它,而且自從我與TM積極合作已經有相當長的時間了)。 1線程的失敗似乎很好奇 - 如果將線程連接到核心,它仍然失敗嗎?同樣,粗略的鎖定結果有點好奇。如果你退出(spinwait)會發生什麼? –

+0

即使將線程固定到核心時也會失敗。有人建議,因爲tlb丟失或緩存丟失。因爲事務中止返回到它的起點,並且好像中斷從不發生。所以下次會發生錯誤。 –

+0

這似乎非常尷尬。 :S對不起,唉,我想我們已經用盡了我的專業知識! –

回答

0

RTM硬件文檔建議如下:

EAX的值可以是 '0' 以下的RTM中止。例如,在RTM區域內使用時的CPUID指令導致事務中止,並且可能不滿足設置任何EAX位的要求。這可能會導致EAX值爲'0'。

(其中,EAX是用於傳達狀態硬件寄存器,即GCC將依次回報給您的返回值)

+0

感謝您的回答,但我不認爲CPUID或SYSTEMCALL會導致問題。我重新編輯我的問題並觸摸整個代碼。你能解釋爲什麼需要後備粗鎖嗎? –

+0

看起來有一堆東西會導致[aborts](http://www.realworldtech.com/haswell-tm/2/)。 –