: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;
}
サプリメント:
- すべてのアカウント整列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)){ } }
うーん。私はTMを積極的に働いて以来、かなり時間がかかりました。 1つのスレッドの失敗は不思議そうです - スレッドをコアに固定するとまだ失敗していますか?同様に、粗いロックの結果はちょっと不思議です。あなたが代わりに(spinwait)離れるとどうなりますか? –
スレッドをコアに固定しても失敗します。誰かが、tlbが見つからなかったか、キャッシュが失われている可能性があります。トランザクションのアボートはその開始点に戻り、割り込みが発生しないようにします。だから間違いは次回に起こります。 –
それは非常に扱いにくいようです。 :S申し訳ありません、悲しいかな、私はここで私の専門知識を使い果たしたと思います! –