x86_64 - 为什么用 rdtsc/rdtscp 给程序计时会给出不合理的大数字?

Lui*_*ins 2 assembly x86-64 rdtsc

我正在尝试使用 rdtscp 为子例程计时。这是我的程序:

; Setting up time
rdtscp                      ; Getting time
push rax                    ; Saving timestamp

; for(r9=0; r9<LOOP_SIZE; r9++)
mov r9, 0
lup0:
call subr
inc r9
cmp r9, LOOP_SIZE
jnz lup0

; Calculating time taken
pop rbx                     ; Loading old time
rdtscp                      ; Getting time
sub rax, rbx                ; Calculating difference
Run Code Online (Sandbox Code Playgroud)

如果LOOP_SIZE足够小,我会得到一致和预期的结果。但是,当我让它足够大(大约 10^9)时,我会从 10^9 飙升到 10^20。

; Result with "LOOP_SIZE equ 100000000"
971597237
; Result with "LOOP_SIZE equ 1000000000"
18446744072281657066
Run Code Online (Sandbox Code Playgroud)

我用来显示数字的方法将它们显示为无符号,所以我想象显示的大数字实际上是一个负数并且发生了溢出。然而,971597237甚至还没有接近 64 位整数的限制,所以,假设问题是溢出,为什么会发生呢?

Lui*_*ins 7

问题是,根据文档,即使在 64 位模式下, 的值rdtscp也没有存储在 上rax,而是存储在edx:eax(这意味着高位打开edx,低位打开eax)。

因此,如果您想在 上使用完整的 64 位值rax,则必须从 中移动较高位edx

; Setting up time
rdtscp                      ; Getting time
shl rdx, 32                 ; Shifting rdx to the correct bit position
add rax, rdx                ; Adding both to make timestamp
push rax                    ; Saving timestamp

; [...stuff...]

; Calculating time taken
rdtscp                      ; Getting time
pop rbx                     ; Loading old time (below rdtscp)
shl rdx, 32                 ; Shifting rdx to the correct bit position
add rax, rdx                ; Adding both to make timestamp
sub rax, rbx                ; Calculating difference
Run Code Online (Sandbox Code Playgroud)

编辑:pop rbx向下移动一行,在rdtscp. 正如 Peter 所指出的,某些寄存器(rax、rdx 和 rcx)可能会被rdtscp. 在您的示例中,这不是问题,但是如果您决定pop rcx改为使用,则它可能会被 覆盖rdtscp,因此最好仅在其之后弹出堆栈。


此外,您可以通过将旧时间戳保存在您的子例程不使用的寄存器中来避免对堆栈的两次调用:

; Setting up time
rdtscp                      ; Getting time
shl rdx, 32                 ; Shifting rdx to the correct bit position
lea r12, [rdx + rax]        ; Adding both to make timestamp, and saving it

; [...stuff (that doesn't use r12)...]

; Calculating time taken
rdtscp                      ; Getting time
shl rdx, 32                 ; Shifting rdx to the correct bit position
add rax, rdx                ; Adding both to make timestamp
sub rax, r12                ; Calculating difference
Run Code Online (Sandbox Code Playgroud)

  • `add rax, rdx` / `mov r8, rax` 可以替换为 `lea r8, [rdx + rax]` 就像我最初建议的那样,这在所有 CPU 上基本上在所有可能重要的方面都更便宜。另外,如果您要计时的是“子例程”,如果它遵循标准调用约定,您可能需要选择一个调用保留寄存器,例如“r12”。 (2认同)