Wak*_*Leo 5 c++ x86 transactional-memory intel-tsx
通过 gcc 文档:x86-transactional-memory-intrinsics.html,当事务失败/中止时,_xbegin()应返回abort status。但是,我发现它有时返回 0。而且出现的频率非常高。什么情况下**_xbegin()**会返回0?
\n\n查看手册后,我发现很多情况都可能导致这种结果。例如,CPUID、SYSTEMCALL、CFLUSH.等。但是,我不认为我的代码触发了其中任何一个。
\n\n这是我的代码: 模拟一家小银行,随机帐户将 1 美元转账到另一个帐户。
\n\n#include "immintrin.h"\n#include <thread>\n#include <unistd.h>\n#include <iostream>\n\nusing namespace std;\n\n#define n_threads 1\n#define OPSIZE 1000000000\ntypedef struct Account{\n long balance;\n long number;\n} __attribute__((aligned(64))) account_t;\n\ntypedef struct Bank{\n account_t* accounts;\n long size;\n} bank_t;\n\nbool done = 0;\nlong *tx, *_abort, *capacity, *debug, *failed, *conflict, *zero;\n\nvoid* f1(bank_t* bank, int id){\n for(int i=0; i<OPSIZE; i++){ \n int src = rand()%bank->size;\n int dst = rand()%bank->size;\n while(src == dst){\n dst = rand()%bank->size;\n } \n\n while(true){\n unsigned stat = _xbegin();\n if(stat == _XBEGIN_STARTED){\n bank->accounts[src].balance++; \n bank->accounts[dst].balance--;\n _xend();\n asm volatile("":::"memory"); \n tx[id]++;\n break;\n }else{\n _abort[id]++;\n\n if (stat == 0){\n zero[id]++;\n }\n if (stat & _XABORT_CONFLICT){\n conflict[id]++;\n }\n if (stat & _XABORT_CAPACITY){\n capacity[id]++;\n }\n if (stat & _XABORT_DEBUG){\n debug[id]++;\n }\n if ((stat & _XABORT_RETRY) == 0){\n failed[id]++;\n break;\n }\n if (stat & _XABORT_NESTED){\n printf("[ PANIC ] _XABORT_NESTED\\n");\n exit(-1);\n }\n if (stat & _XABORT_EXPLICIT){\n printf("[ panic ] _XBEGIN_EXPLICIT\\n");\n exit(-1);\n }\n }\n }\n }\n return NULL;\n}\nvoid* f2(bank_t* bank){\n printf("_heartbeat function\\n");\n long last_txs=0, last_aborts=0, last_capacities=0, last_debugs=0, last_faileds=0, last_conflicts=0, last_zeros = 0;\n long txs=0, aborts=0, capacities=0, debugs=0, faileds=0, conflicts=0, zeros = 0;\n while(1){\n last_txs = txs;\n last_aborts = aborts;\n last_capacities = capacities;\n last_debugs = debugs;\n last_conflicts = conflicts;\n last_faileds = faileds;\n last_zeros = zeros;\n\n txs=aborts=capacities=debugs=faileds=conflicts=zeros = 0;\n for(int i=0; i<n_threads; i++){\n txs += tx[i];\n aborts += _abort[i];\n faileds += failed[i];\n capacities += capacity[i];\n debugs += debug[i];\n conflicts += conflict[i];\n zeros += zero[i];\n }\n\n printf("txs\\t%ld\\taborts\\t\\t%ld\\tfaileds\\t%ld\\tcapacities\\t%ld\\tdebugs\\t%ld\\tconflit\\t%ld\\tzero\\t%ld\\n", \n txs - last_txs, aborts - last_aborts , faileds - last_faileds, \n capacities- last_capacities, debugs - last_debugs, conflicts - last_conflicts,\n zeros- last_zeros);\n\n sleep(1);\n }\n}\n\nint main(int argc, char** argv){\n int accounts = 10240;\n\n bank_t* bank = new bank_t;\n bank->accounts = new account_t[accounts];\n bank->size = accounts;\n\n for(int i=0; i<accounts; i++){\n bank->accounts[i].number = i;\n bank->accounts[i].balance = 0;\n }\n\n thread* pid[n_threads];\n tx = new long[n_threads];\n _abort = new long[n_threads];\n capacity = new long[n_threads];\n debug = new long[n_threads];\n failed = new long[n_threads];\n conflict = new long[n_threads];\n zero = new long[n_threads];\n\n thread* _heartbeat = new thread(f2, bank);\n for(int i=0; i<n_threads; i++){\n tx[i] = _abort[i] = capacity[i] = debug[i] = failed[i] = conflict[i] = zero[i] = 0;\n pid[i] = new thread(f1, bank, i);\n }\n\n// sleep(5);\n for(int i=0; i<n_threads;i++){\n pid[i]->join();\n }\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n补充品:
\n\nasm volatile("":::"memory");因此不存在指令重新排序问题。中止率有时会增加。这是结果
\n\ntxs 84 aborts 0 faileds 0 capacities 0 debugs 0 conflit 0 zero 0\ntxs 17070804 aborts 71 faileds 68 capacities 9 debugs 0 conflit 3 zero 59\ntxs 58838 aborts 9516662 faileds 9516661 capacities 0 debugs 0 conflit 1 zero 9516661\ntxs 0 aborts 9550428 faileds 9550428 capacities 0 debugs 0 conflit 0 zero 9550428\ntxs 0 aborts 9549254 faileds 9549254 capacities 0 debugs 0 conflit 0 zero 9549254\nRun Code Online (Sandbox Code Playgroud)即使n_threads为1,结果也是一样的。
如果我在回退后添加粗略锁定,如下所示,结果似乎是正确的。
\n\nint fallback_lock;\n\nbool \nrtm_begin(int id)\n{ \n while(true) { \n unsigned stat;\n stat = _xbegin ();\n if(stat == _XBEGIN_STARTED) {\n return true;\n } else {\n _abort[id]++;\n if (stat == 0){\n zero[id]++;\n }\n //call some fallback function\n if (stat& _XABORT_CONFLICT){\n conflict[id]++;\n }\n\n //will not succeed on a retry\n if ((stat & _XABORT_RETRY) == 0) {\n failed[id]++;\n //grab a fallback lock\n while (!__sync_bool_compare_and_swap(&fallback_lock,0,1)) {\n }\n return false;\n }\n }\n }\n}\n....\n\nin_rtm = rtm_begin(id);\ny = fallback_lock;\naccounts[src].balance--;\naccounts[dst].balance++;\nif (in_rtm){\n _xend();\n}else{\n while(!__sync_bool_compare_and_swap(&fallback_lock, 1, 0)){\n }\n}\nRun Code Online (Sandbox Code Playgroud)