通過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;
}
補充:
- 所有賬戶的64位對齊。我打印銀行 - >賬戶[0],銀行 - >賬戶1地址。 0xf41080,0xf410c0。
- 使用-O0和
asm volatile("":::"memory");
因此沒有指令重新排序的問題。 中止率在時間上升。這裏是結果
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
即使通過n_threads是1,結果也是一樣的。
如果我在後退後添加粗體鎖,則結果看起來是正確的。
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)){ } }
嗯。 Cursory檢查表明事情沒有問題(儘管我沒有運行它,而且自從我與TM積極合作已經有相當長的時間了)。 1線程的失敗似乎很好奇 - 如果將線程連接到核心,它仍然失敗嗎?同樣,粗略的鎖定結果有點好奇。如果你退出(spinwait)會發生什麼? –
即使將線程固定到核心時也會失敗。有人建議,因爲tlb丟失或緩存丟失。因爲事務中止返回到它的起點,並且好像中斷從不發生。所以下次會發生錯誤。 –
這似乎非常尷尬。 :S對不起,唉,我想我們已經用盡了我的專業知識! –