2016-07-05 5 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. Все счета выровнен 64bit. Я распечатал банк-> счета [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

Хм. Беглый осмотр показывает, что все в порядке (хотя я его не запускал, и прошло довольно много времени с тех пор, как я активно работал с ТМ). Кажется любопытным неудача с 1 нитью - все еще не удается, если вы привяжете нить к ядру? Аналогично, результат с крупным замком немного любопытен. Что произойдет, если вы отступите (спинвейт)? –

+0

Это не удается даже при закреплении нити к сердечнику. Кто-то предполагает, что это возможно, потому что отсутствует или отсутствует кеш. Поскольку прерывание транзакции возвращается к начальной точке и как будто прерывание никогда не происходит. Так что ошибка случится в следующий раз. –

+0

Это кажется очень неудобным. : S К сожалению, увы, я думаю, что мы исчерпали свой опыт здесь! –

ответ

0

Аппаратная документация на RTM предлагает следующее:

Значение EAX может быть '0' после RTM прерывания. Например, команда CPUID, используемая внутри региона RTM, вызывает прерывание транзакции и может не удовлетворять требованиям для установки любого из битов EAX. Это может привести к значению EAX '0'.

(Где, EAX это регистр оборудования, используемого для связи статус, что GCC будет в свою очередь, вернуться к вам в качестве возвращаемого значения)

+0

Спасибо за ваш ответ, но я не думаю, что CPUID или SYSTEMCALL вызывают проблему. Я переписываю свой вопрос и касаюсь всего кода. Можете ли вы объяснить, зачем нужен резервный грубый замок? –

+0

Похоже, что есть куча вещей, которые вызывают [прерывания] (http://www.realworldtech.com/haswell-tm/2/). –