硬件事务内存:_xbegin() 返回 0

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}\n
Run Code Online (Sandbox Code Playgroud)\n\n

补充品:

\n\n
    \n
  1. 所有帐户都是 64 位对齐的。我打印了bank->accounts[0],bank->accounts 1地址。0xf41080\xef\xbc\x8c0xf410c0\xe3\x80\x82
  2. \n
  3. 使用-O0asm volatile("":::"memory");因此不存在指令重新排序问题。
  4. \n
  5. 中止率有时会增加。这是结果

    \n\n
    txs     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\n
    Run Code Online (Sandbox Code Playgroud)
  6. \n
  7. 即使n_threads为1,结果也是一样的。

  8. \n
  9. 如果我在回退后添加粗略锁定,如下所示,结果似乎是正确的。

    \n\n
    int 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}\n
    Run Code Online (Sandbox Code Playgroud)
  10. \n
\n

Mat*_* G. 1

RTM的硬件文档建议如下:

RTM 中止后,EAX 的值可以为“0”。例如,CPUID 指令在 RTM 区域内使用时会导致事务中止,并且可能无法满足设置任何 EAX 位的要求。这可能会导致 EAX 值为“0”。

(其中,EAX是用于传达状态的硬件寄存器,GCC将依次返回给您作为 的返回值)