代码之家  ›  专栏  ›  技术社区  ›  RTC222

lock cmpxchg无法按核心顺序执行线程

  •  0
  • RTC222  · 技术社区  · 6 年前

    Spin_lock:
    xor rax,rax
    lock cmpxchg [spin_lock_core],rbx
    jnz Spin_lock
    
    ; Test
    mov rbp,extra_test_array
    mov [rbp+rbx],rbx
    
    ; Execute some code before looping out
    mov rax,1234
    mov rdx,23435
    add rax,rbx
    mov rcx,rax
    ;jmp label_899
    
    mov rax,rbx
    add rax,8
    xchg [spin_lock_core],rax
    

    下面是完整的、最少的代码(在本例中尽可能少地使用NASM)。代码是为一个共享对象编写的,如果它得到一个输入数组,则代码是可复制的(编写时,输入数组是int还是float并不重要):

    ; Header Section
    [BITS 64]
    
    [default rel]
    
    global Main_Entry_fn
    extern pthread_create, pthread_join, pthread_exit, pthread_self,    sched_getcpu
    global FreeMem_fn
    extern malloc, realloc, free
    extern sprintf
    
    section .data align=16
    X_ctr: dq 0
    data_master_ptr: dq 0
    initial_dynamic_length: dq 0
    XMM_Stack: dq 0, 0, 0, 0, 0, 0, 0
    ThreadID: dq 0
    X_ptr: dq 0
    X_length: dq 0
    X: dq 0
    collect_ptr: dq 0
    collect_length: dq 0
    collect_ctr: dq 0
    even_squares_list_ptrs: dq 0, 0, 0, 0
    even_squares_list_ctr: dq 0
    even_squares_list_length: dq 0
    Number_Of_Cores: dq 32
    pthread_attr_t: dq 0
    pthread_arg: dq 0
    Join_Ret_Val: dq 0
    tcounter: dq 0
    sched_getcpu_array: times 4 dq 0
    ThreadIDLocked: dq 0
    spin_lock_core: dq 0
    extra_test_array: dq 0
    
    ; __________
    
    section .text
    
    Init_Cores_fn:
    
    ; _____
    ; Create Threads
    
    label_0:
    
    mov rdi,ThreadID            ; ThreadCount
    mov rsi,pthread_attr_t  ; Thread Attributes
    mov rdx,Test_fn         ; Function Pointer
    mov rcx,pthread_arg
    call pthread_create wrt ..plt
    
    mov rdi,[ThreadID]      ; id to wait on
    mov rsi,Join_Ret_Val        ; return value
    call pthread_join wrt ..plt
    
    mov rax,[tcounter]
    add rax,8
    mov [tcounter],rax
    mov rbx,[Number_Of_Cores]
    cmp rax,rbx
    jl label_0
    
    ; _____
    
    jmp label_900 ; All threads return here, and exit
    
    ; ______________________________________
    
    Test_fn:
    
    ; Get the core number
    call sched_getcpu wrt ..plt
    mov rbx,8 ; multiply by 8
    mul rbx
    push rax
    
    pop rax
    mov rbx,rax
    push rax
    
    Spin_lock:
    lock cmpxchg [spin_lock_core],rbx
    jnz Spin_lock
    
    ; Test
    mov rbp,extra_test_array
    mov [rbp+rbx],rbx
    
    ; Execute some code before looping out
    mov rax,1234
    mov rdx,23435
    add rax,rbx
    mov rcx,rax
    jmp label_899
    
    mov rax,rbx
    add rax,8
    xchg [spin_lock_core],rax
    
    ;__________
    
    label_899:
    
    pop rax
    
    ret
    
    ; __________
    
    label_900:
    
    mov rdi,extra_test_array ;audit_array
    mov rax,rdi
    
    ret
    
    ;__________
    ;Free the memory
    
    FreeMem_fn:
    
    ;The pointer is passed back in rcx (of course)
    
    sub rsp,40
    call free wrt ..plt
    add rsp,40
    ret
    
    ; __________
    ; Main Entry
    
    
    Main_Entry_fn:
    push rdi
    push rbp
    push rbx
    push r15
    xor r15,r15
    push r14
    xor r14,r14
    push r13
    xor r13,r13
    push r12
    xor r12,r12
    push r11
    xor r11,r11
    push r10
    xor r10,r10
    push r9
    xor r9,r9
    push r8
    xor r8,r8
    movsd [XMM_Stack+0],xmm13
    movsd [XMM_Stack+8],xmm12
    movsd [XMM_Stack+16],xmm11
    movsd [XMM_Stack+24],xmm15
    movsd [XMM_Stack+32],xmm14
    movsd [XMM_Stack+40],xmm10
    mov [X_ptr],rdi
    mov [data_master_ptr],rsi
    ; Now assign lengths
    lea rdi,[data_master_ptr]
    mov rbp,[rdi]
    xor rcx,rcx
    movsd xmm0,qword[rbp+rcx]
    cvttsd2si rax,xmm0
    mov [X_length],rax
    add rcx,8
    
    ; __________
    ; Write variables to assigned registers
    
    mov r15,0
    lea rdi,[rel collect_ptr]
    mov r14,qword[rdi]
    mov r13,[collect_ctr]
    mov r12,[collect_length]
    lea rdi,[rel X_ptr]
    mov r11,qword[rdi]
    mov r10,[X_length]
    
    ; __________
    
    call Init_Cores_fn
    
    movsd xmm10,[XMM_Stack+0]
    movsd xmm14,[XMM_Stack+8]
    movsd xmm15,[XMM_Stack+16]
    movsd xmm11,[XMM_Stack+24]
    movsd xmm12,[XMM_Stack+32]
    movsd xmm13,[XMM_Stack+40]
    pop r8
    pop r9
    pop r10
    pop r11
    pop r12
    pop r13
    pop r14
    pop r15
    pop rbx
    pop rbp
    pop rdi
    ret
    

    指令“lock cmpxchg”应该在[spin_lock_core]变量更新之前失败,但它不会这样做。

    Spin_lock:
    xor rax,rax
    lock cmpxchg [spin_lock_core],rbx
    jnz Spin_lock
    

    随着这一变化,它冻结了,正如预期的那样。但是当我移除jmp标签_899时,它仍然会冻结,但它不应该这样做。

    编辑122219:

    根据昨天对这个问题的评论,我修改了自旋锁代码,以(1)消除原子操作,支持更快的mov和cmp指令,(2)为每个内核分配唯一的内存位置,以及(3)通过>256字节以避免相同缓存线上的内存。

    当前一个核心完成时,每个核心的内存位置将更改为1。当每个核心完成时,它将自己的内存位置设置回0。

    如果我在自旋锁之前循环了所有其他核心,那么代码将成功执行核心0。当我让所有四个内核通过自旋锁运行时,程序再次挂起。

    我已经验证了当前一个内核完成时,每个单独的内存位置都被设置为1。

    以下是更新的自旋锁部分:

    section .data
    spin_lock_core: times 140 dq 0
    spin_lock_core_offsets: dq 0,264,528,792
    
    section .text
    
    ; Calculate the offset to spin_lock_core
    mov rbp,spin_lock_core
    mov rdi,spin_lock_core_offsets
    mov rax,[rdi+rbx]
    add rbp,rax
    
    ; ________
    
    Spin_lock:
    pause
    cmp byte[rbp],1
    jnz Spin_lock
    
    xor rax,rax
    mov [rbp],rax ; Set current memory location to zero
    
    ; Execute some code before looping out
    mov rax,1234
    mov rdx,23435
    add rax,rdx
    mov rcx,rax
    
    ; Loop out if this is the last core
    mov rax,rbx
    add rax,8
    cmp rax,[Number_Of_Cores]
    jge label_899
    
    ; Set next core to 1 by adding 264 to the base address
    add rbp,264
    mov rax,1
    mov [rbp],rax
    

    为什么这个代码仍然挂起?

    0 回复  |  直到 6 年前
        1
  •  2
  •   prl    6 年前

    我认为你根本不应该使用cmpxchg。试试这个:

    Spin_lock:
    pause
    cmp [spin_lock_core],rbx
    jnz Spin_lock
    
    ; Test
    mov rbp,extra_test_array
    mov [rbp+rbx],rbx
    
    ; Execute some code before looping out
    mov rax,1234
    mov rdx,23435
    add rax,rbx
    mov rcx,rax
    ;jmp label_899
    
    lea rax,[rbx+8]
    mov [spin_lock_core],rax
    
        2
  •  0
  •   RTC222    6 年前

    我解决了这个自旋锁问题,但在Peter Cordes下面的评论之后,我发现它是不正确的。我不会删除这个答案,因为我希望它能带来解决方案。

    https://www.felixcloutier.com/x86/cmpxchg )将格式显示为CMPXCHG r/m64,r64,但源操作数不能是任何r64——它必须是rax,正如该条目所说。

    在没有“mov-rax,rbx”行的情况下,它可以工作,因为在第一次迭代中,rax寄存器被设置为与内存位置匹配的0。默认情况下,第二次迭代成功。

    当我添加“mov-rax,rbx”——重置rax——程序再次挂起。我真的很感激任何关于为什么这个程序应该像写的那样挂起的想法。

    在该块的开头,rbx是核心编号:

    section .data
    spin_lock_core: times 4 dq 0
    
    section .text
    
    [ Code leading up to this spinlock section shown above ]
    
    mov rbp,spin_lock_core
    
    Spin_lock:
    pause
    mov rax,rbx
    lock cmpxchg [rbp+rbx],rax
    jnz Spin_lock
    
    mov rax,rbx
    add rax,8
    cmp rax,[Number_Of_Cores]
    jge spin_lock_out
    
    xchg [rbp+rax],rax
    
    spin_lock_out:
    

    与我原来的帖子不同的是:

    1. 每个内核都在自己独特的内存位置上旋转(并从中读取)。

    2. 每个唯一的内存位置都按核心顺序更新。