2016-07-05 9 views
2

x86-transactional-memory-intrinsics.html、トランザクションが失敗したとき/中止、_xbegin()アボート状態を返す必要があります。しかし、私は時々それが0を返すことがわかります。周波数は非常に高いです。 ** _ xbegin()**が0を返すのはどのような状況ですか?ハードウェアトランザクショナルメモリ:gccのドキュメントによって_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_threadsが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

うーん。私はTMを積極的に働いて以来、かなり時間がかかりました。 1つのスレッドの失敗は不思議そうです - スレッドをコアに固定するとまだ失敗していますか?同様に、粗いロックの結果はちょっと不思議です。あなたが代わりに(spinwait)離れるとどうなりますか? –

+0

スレッドをコアに固定しても失敗します。誰かが、tlbが見つからなかったか、キャッシュが失われている可能性があります。トランザクションのアボートはその開始点に戻り、割り込みが発生しないようにします。だから間違いは次回に起こります。 –

+0

それは非常に扱いにくいようです。 :S申し訳ありません、悲しいかな、私はここで私の専門知識を使い果たしたと思います! –

答えて

0

RTM上のハードウェアマニュアルは、以下を示唆する:

EAXの値がRTMアボート以下 '0' とすることができます。たとえば、RTM領域内で使用される場合、CPUID命令はトランザクションアボートを引き起こし、EAXビットのいずれかを設定するための要件を満たさない可能性があります。これは、EAX値が「0」になる可能性があります。

は(どこで、EAXは、状況を通信するために使用されるハードウェアレジスタであるGCC意志の戻り値としてあなたにターンリターンで)

+0

あなたの答えをありがとうが、私はCPUIDまたはSYSTEMCALL問題を引き起こすとは思わない。私は質問を再編集し、コード全体に触れます。フォールバックの粗いロックが必要な理由を説明できますか? –

+0

[abort](http://www.realworldtech.com/haswell-tm/2/)の原因となるものがたくさんあるように見えます。 –

関連する問題