NASM x64 на macOS с использованием mach_absolute_time - проблема с получением наносекунд (обзор рабочего кода) - PullRequest
0 голосов
/ 26 апреля 2020

Я просто изучаю NASM, так что извините, если я делаю очевидную ошибку, но я не могу понять, что я делаю неправильно.

Пожалуйста, посмотрите код ниже и дайте мне знать, что является неправильным. Он компилируется и работает нормально, но в результате печатает мусор. Я знаю, что информация, поступающая из mach_absolute_time, зависит от аппаратного обеспечения, и поэтому ее необходимо скорректировать с помощью информации из структуры из mach_timebase_info.

. Я создал тестовую программу, которая искусственно занимает 1 сек * 1014. * выполнить. Он печатает информацию об абсолютном времени начала, окончания и истекшего времени машины (что любопытно, что в моей машине отображается правильное количество наносекунд). Но вычисленные наносекунды - это мусор - вероятно, связанный с некоторой ошибкой, которую я делаю с математикой / использованием регистров xmm и размеров данных, но, ради любви ко мне, не могу понять это. Спасибо за помощь!

Пример запуска: enter image description here

; ----------------------------------------------------------------------------------------
; Testing mach_absolute_time
; nasm -fmacho64 mach.asm && gcc -o mach mach.o
; ----------------------------------------------------------------------------------------

    global      _main
    extern      _printf
    extern      _mach_absolute_time
    extern      _mach_timebase_info
    extern      _nanosleep
    default     rel

section .text

_main: 
    push        rbx                     ; aligns the stack x C calls

    ; start measurement
    call        _mach_absolute_time     ; get the absolute time hardware dependant
    mov         [start], rax            ; save start in start
    ; print start
    lea         rdi, [time_absolute]
    mov         rsi, rax
    call        _printf

    ; do some time intensive stuff - This simulates 1 sec work
    lea         rdi, [timeval]
    call        _nanosleep

    ; end measurement 
    call        _mach_absolute_time
    mov         [end], rax
    ; print end
    lea         rdi, [time_absolute]
    mov         rsi, rax
    call        _printf

    ; calc elapsed
    mov         r10d, [end]
    mov         r11d, [start]
    sub         r10d, r11d              ; r10d = end - start
    mov         [diff], r10d            ; copy to diff
    mov         rax, [diff]             ; diff to rax to print as int
    cvtsi2ss    xmm2, r10d              ; diff to xmm2 to calc nanoseconds
    ; print elapsed
    lea         rdi, [diff_absolute]
    mov         rsi, rax
    call        _printf

    ; get conversion factor to get nanoseconds and store numerator and denominator
    ; in xmm0 and xmm1
    lea         rdi, [timebase_info]
    call        _mach_timebase_info     ; get conversion factor to nanoseconds
    movss       xmm0, [numer]
    movss       xmm1, [denom]
    ; print numerator & denominator as float to ensure I am getting the info into xmm regs
    lea         rdi, [time_base]
    mov         rax, 2
    call        _printf

    ; calc nanoseconds - xmm0 ends with nanoseconds
    mulss       xmm0, xmm2              ; multiply elapsed * numerator
    divss       xmm0, xmm1              ; divide by the denominator
    ; print nanoseconds as float
    lea         rdi, [nanosecs_calc]
    mov         rax, 1                  ; 1 non-int argument
    call        _printf

    pop         rbx                     ; undoes the stack alignment push
    ret

section .data

; _mach_timebase_info call struct 
timebase_info:
    numer      db 8
    denom      db 8

; lazy way to set up 1 sec wait
timeval:
    tv_sec      dq 1
    tv_usec     dq 0

time_absolute:  db "mach_absoute_time: %ld", 10, 0
diff_absolute:  db "absoute_time diff: %ld", 10, 0
time_base:      db "numerator: %g, denominator: %g", 10, 0
nanosecs_calc:  db "calc nanoseconds:  %ld", 10, 0
; using %g format also prints garbage
; nanosecs_calc:  db "calc nanoseconds:  %g", 10, 0

; should use registers but for clarity
start:          dq 0
end:            dq 0
diff:           dq 0

1 Ответ

0 голосов
/ 26 апреля 2020

РЕДАКТИРОВАТЬ: Я знаю, что было не так. xmm reges очищаются после c вызовов, поэтому умножение и результат не удаются. В любом случае, обходной путь C, приведенный ниже, чтобы получить соотношение временной базы, работает нормально, с полным кодом теста ниже.

Обходной путь - получить соотношение из mach_timebase_info из короткой функции C и умножить его на результат mach_absolute_time, чтобы получить наносекунды.

Как и предполагалось в моем действительном аппаратное обеспечение (конец 2013 г. MBP 2.3 i7), mach_absolute_time уже возвращает наносекунды, поэтому коэффициент, напечатанный C, равен 1.000. (числитель временной базы = 1, знаменатель тимабазы ​​= 1)

#include <stdio.h>
#include <mach/mach_time.h>

double timebase() {
    double  ratio;
    mach_timebase_info_data_t tb;

    mach_timebase_info(&tb);
    ratio = tb.numer / tb.denom;
    printf("num: %u, den: %u\n", tb.numer, tb.denom);
    printf("ratio from C: %.3f\n", ratio);

    return ratio;
}

NASM:

    global      _main
    extern      _printf
    extern      _mach_absolute_time
    extern      _timebase
    extern      _nanosleep
    default     rel

section .text

_main: 
    push        rbx                     ; aligns the stack x C calls

    ; start measurement
    call        _mach_absolute_time     ; get the absolute time hardware dependant
    mov         [start], rax            ; save start in start
    ; print start
    lea         rdi, [time_absolute]
    mov         rsi, rax
    call        _printf

    ; do some time intensive stuff - This simulates 1 sec work
    lea         rdi, [timeval]
    call        _nanosleep

    ; end measurement 
    call        _mach_absolute_time
    mov         [end], rax
    ; print end
    lea         rdi, [time_absolute]
    mov         rsi, rax
    call        _printf

    ; calc elapsed
    mov         r10d, [end]
    mov         r11d, [start]
    sub         r10d, r11d              ; r10d = end - start
    mov         [diff], r10d            ; copy to diff
    mov         rax, [diff]             ; diff to rax to print as int 

    ; print elapsed
    lea         rdi, [diff_absolute]
    mov         rsi, [diff]
    call        _printf

    ; get conversion ratio from C function
    call        _timebase               ; get conversion ratio to nanoseconds into xmm0    
    cvtsi2sd    xmm1, [diff]            ; load diff from mach_absolute time in [diff]
                                        ; if you do it before register gets cleared
    ; calc nanoseconds - xmm0 ends with nanoseconds
    ; in my hardware ratio is 1.0 so mach_absolute_time = nanoseconds
    mulsd       xmm0, xmm1
    cvtsd2si    rax, xmm0                
    mov         [result], rax           ; save to result

    ; print nanoseconds as int
    lea         rdi, [nanosecs_calc]
    mov         rsi, [result]
    call        _printf

    pop         rbx                     ; undoes the stack alignment push
    ret

section .data

; lazy way to set up 1 sec wait
timeval:
    tv_sec      dq 1
    tv_usec     dq 0

time_absolute:  db "mach_absoute_time: %ld", 10, 0
diff_absolute:  db "absoute_time diff: %ld", 10, 0
nanosecs_calc:  db "nanoseconds:       %ld", 10, 0

; should use registers but for clarity
start:          dq 0
end:            dq 0
diff:           dq 0
result:         dq 0
...