x86_64 - 为什么用 rdtsc/rdtscp 给程序计时会给出不合理的大数字?
x86_64 - Why is timing a program with rdtsc/rdtscp giving unreasonably large numbers?
我正在尝试使用 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
如果 LOOP_SIZE
足够小,我会得到一致的预期结果。然而,当我让它足够大(大约 10^9)时,我从 10^9 飙升到 10^20。
; Result with "LOOP_SIZE equ 100000000"
971597237
; Result with "LOOP_SIZE equ 1000000000"
18446744072281657066
我用来显示数字的方法将它们显示为无符号,所以我想显示的大数字实际上是负数并且发生了溢出。但是,971597237
甚至没有接近 64 位整数限制,因此,假设问题是溢出,为什么会发生?
问题是as per documentation,rdtscp
的值没有存储在rax
上,而是存储在edx:eax
上(也就是说高位在edx
和 eax
上的低位)即使在 64 位模式下也是如此。
所以,如果你想在 rax
上使用完整的 64 位值,你必须从 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
编辑:将 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
我正在尝试使用 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
如果 LOOP_SIZE
足够小,我会得到一致的预期结果。然而,当我让它足够大(大约 10^9)时,我从 10^9 飙升到 10^20。
; Result with "LOOP_SIZE equ 100000000"
971597237
; Result with "LOOP_SIZE equ 1000000000"
18446744072281657066
我用来显示数字的方法将它们显示为无符号,所以我想显示的大数字实际上是负数并且发生了溢出。但是,971597237
甚至没有接近 64 位整数限制,因此,假设问题是溢出,为什么会发生?
问题是as per documentation,rdtscp
的值没有存储在rax
上,而是存储在edx:eax
上(也就是说高位在edx
和 eax
上的低位)即使在 64 位模式下也是如此。
所以,如果你想在 rax
上使用完整的 64 位值,你必须从 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
编辑:将 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