gpt4 book ai didi

assembly - 如何使用 XACQUIRE、XRELEASE Hardware Lock Elision (HLE) 前缀提示?

转载 作者:行者123 更新时间:2023-12-03 19:46:53 26 4
gpt4 key购买 nike

只是为了学习这个,我试图掌握如何使用HLE prefixes XACQUIREXRELEASE .阅读英特尔文档后,我的理解是在使用 XACQUIRE 执行指令后前缀 CPU 进入某种写锁,直到带有 XRELEASE 的指令字首。所以我写了下面的测试代码,看看我是否正确。嗯,还有一些我不明白的地方,因为我的代码示例失败了。

那么有人可以告诉我这些 HLE 前缀遗漏了什么吗?

两次失败:

  • xtest指令报告未启用 HLE,并且
  • 因为我假设的“mutex-ed”代码不作为互斥体运行,所以它无法并发。

  • 接下来是Windows C++项目,用VS 2017编译的x64 .asm文件如下:
    .code

    testCPUID PROC
    push rbx

    ; CPUID.07h.EBX.HLE[bit 4]==1

    mov eax, 7h
    xor ecx, ecx
    cpuid
    and rbx, 1 shl 4

    mov rax, rbx
    pop rbx
    ret
    testCPUID ENDP



    testHLEWrite PROC
    ; RCX = pointer to TST91 struct:
    ; void* pPtrToNextWrite;
    ; int nNextValue;
    ; void* pCutoffPtr;
    ; void* pBeginPtr;

    xor edx, edx
    xacquire xchg [rcx], rdx ; I'm assuming that this will work as a mutex ...

    xtest ; Sanity check to see if HLE got enabled?
    jnz lbl_00 ; If HLE is on => ZF=0
    int 3 ; we get here if HLE did not get enabled
    lbl_00:

    ; Do some nonsensical stuff
    ; The idea is to write sequential values into a shared array
    ; to see if the lock above holds
    ; Format:
    ; > --16 sequential bytes-- <

    mov r8d, dword ptr [rcx + 8]

    mov byte ptr [rdx], '>'
    inc rdx

    ; Write 16 sequential bytes

    mov rax, 10h
    lbl_01:
    mov byte ptr [rdx], r8b
    inc r8
    inc rdx
    dec rax
    jnz lbl_01

    mov byte ptr [rdx], '<'
    inc rdx

    cmp rdx, [rcx + 10h] ; check if reached the end of buffer
    jb lbl_02
    mov rdx, [rcx + 18h] ; reset ptr to the beginning of buffer
    lbl_02:

    mov dword ptr [rcx + 8], r8d
    xrelease mov [rcx], rdx ; this will release the mutex

    ret
    testHLEWrite ENDP





    testHLEForCorrectness PROC
    ; RCX = pointer to TST91 struct:
    ; void* pPtrToNextWrite;
    ; int nNextValue;
    ; void* pCutoffPtr;
    ; void* pBeginPtr;

    xor edx, edx
    xacquire xchg [rcx], rdx ; I'm assuming that this will work as a mutex ...

    xtest ; Sanity check to see if HLE got enabled?
    jnz lbl_00 ; If HLE is on => ZF=0
    int 3 ; we get here if HLE did not get enabled
    lbl_00:

    mov r9, [rcx + 18h]

    lbl_repeat:
    cmp r9, rdx
    jae lbl_out

    cmp byte ptr [r9], '>'
    jnz lbl_bad
    cmp byte ptr [r9 + 1 + 10h], '<'
    jnz lbl_bad

    mov r8b, byte ptr [r9 + 1]
    sub eax, eax
    lbl_01:
    cmp [r9 + rax + 1], r8b
    jnz lbl_bad
    inc rax
    inc r8
    cmp rax, 10h
    jb lbl_01

    add r9, 2 + 10h
    jmp lbl_repeat

    lbl_out:

    xrelease mov [rcx], rdx ; this will release the mutex

    ret

    lbl_bad:
    ; Verification failed
    int 3

    testHLEForCorrectness ENDP

    END

    这是从用户模式 ​​C++ 项目中调用它的方式:
    #include <assert.h>
    #include <Windows.h>

    struct TST91{
    BYTE* pNextWrite;
    int nNextValue;
    BYTE* pCutoffPtr;
    BYTE* pBeginPtr;
    };

    extern "C" {
    BOOL testCPUID(void);
    void testHLEWrite(TST91* p);
    void testHLEForCorrectness(TST91* p);
    };

    DWORD WINAPI ThreadProc01(LPVOID lpParameter);

    TST91* gpStruct = NULL;
    BYTE* gpMem = NULL; //Its size is 'gszcbMemSize' BYTEs
    const size_t gszcbMemSize = 0x1000 * 8;

    int main()
    {
    if(testCPUID())
    {
    gpStruct = new TST91;
    gpMem = new BYTE[gszcbMemSize];

    gpStruct->pNextWrite = gpMem;
    gpStruct->nNextValue = 1;
    gpStruct->pBeginPtr = gpMem;
    gpStruct->pCutoffPtr = gpMem + gszcbMemSize - 0x100;

    for(int t = 0; t < 5; t++)
    {
    CloseThread(CreateThread(NULL, 0,
    ThreadProc01, (VOID*)(1LL << t), 0, NULL));
    }

    _gettch();

    delete gpStruct;
    delete[] gpMem;
    }
    else
    _tprintf(L"Your CPU doesn't support HLE\n");

    return 0;
    }

    DWORD WINAPI ThreadProc01(LPVOID lpParameter)
    {
    if(!SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)lpParameter))
    {
    assert(NULL);
    }

    for(;;)
    {
    testHLEWrite(gpStruct);
    testHLEForCorrectness(gpStruct);
    }

    return 0;
    }

    最佳答案

    你可以回答你自己的问题,不是吗?

    反正。我想我明白了。我会尽量坚持使用简单的英语,或者按照我的理解方式进行。如果我做出了不正确的陈述,请随意编辑它。 (顺便说一句, Hardware Lock Elision ,多酷的名字。听起来像是马特·达蒙的电影。我什至不得不在谷歌上搜索“elision”这个词才能理解它的意思……但我还是不记得了。)

    所以这个HLE概念无非是提示CPU对待lock以更优化的方式添加前缀。 lock对于现代处理器以有效方式执行而言,前缀本身有点“昂贵”。因此,当支持它的 CPU 看到 HLE 前缀时,它最初不会获取锁,但只有在发生读/写冲突时才会这样做。在这种情况下,CPU 将发出 HLE 中止,这反过来将需要稍后的常规锁定。

    此外,XACQUIRE 的 HLE 前缀是 F2 ,并为 XRELEASEF3 ,无非是老派REPNEREP前缀,当与 lock 一起使用时会被忽略- 不支持 HLE 的旧 CPU 的指令。这一切意味着使用 HLE 不需要检查 CPUID其支持的说明,并且可以按原样安全地使用它们。较旧的 CPU 将忽略它们并处理伴随的 lock前缀作为锁,而较新的 CPU 会将它们作为优化提示。换句话说,使用那些 XACQUIREXRELEASE如果您将前缀添加到您自己的互斥锁、信号量的实现中,前缀不会有任何伤害。

    话虽如此,我不得不这样重写我的原始测试代码示例(只是 非常基本的 互斥类型锁的相关并发部分)。

    进入锁的ASM代码:

    testHLEWrite PROC
    ; RCX = pointer to TST91 struct:
    ; void* pPtrToNextWrite;
    ; int nNextValue;
    ; void* pCutoffPtr;
    ; void* pBeginPtr;
    ; size_t lock; <-- new member

    lbl_retry:
    xacquire lock bts qword ptr [rcx + 20h], 1 ; Try to acquire lock (use HLE hint prefix)
    jnc lbl_locked
    pause ; Will issue an implicit HLE abort
    jmp lbl_retry


    lbl_locked:

    然后离开锁:

    (请注意, XRELEASE 前缀与 lock 前缀的不同之处在于它支持具有内存目标操作数的 mov 指令。)
        xrelease mov qword ptr [rcx + 20h], 0       ; Release the lock (use HLE prefix hint)

    ret
    testHLEWrite ENDP

    此外,如果您想使用(Visual Studio 的)内在函数用 C 编写它:
    //Some variable to hold the lock
    volatile long lock = 0;

    然后是代码本身:
    //Acquire the lock
    while(_interlockedbittestandset_HLEAcquire((long *)&lock, 1))
    {
    _mm_pause();
    }

    进而:
    //Leave the lock
    _Store_HLERelease(&lock, 0);

    最后,我想指出,我没有对带有和不带有 HLE 前缀的代码的性能进行任何计时/基准测试。因此,如果有人想这样做(并了解 HLE 概念的有效性),欢迎您这样做。我也会很高兴学习它。

    关于assembly - 如何使用 XACQUIRE、XRELEASE Hardware Lock Elision (HLE) 前缀提示?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51578062/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com