golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
124.13k stars 17.68k forks source link

runtime: use mach_absolute_time for runtime.nanotime #17610

Closed quentinmit closed 7 years ago

quentinmit commented 8 years ago

runtime.nanotime currently returns wallclock time, which means timers do not function correctly in the presence of NTP. We should use mach_absolute_time instead, which returns a monotonic nanoseconds-since-boot timer. (Officially, it returns time in "absolute time units" and you have to multiply by a constant to get nanoseconds. But the implementation of both mach_absolute_time and gettimeofday assume/document that it's really nanoseconds...)

This has the bonus of being much simpler than gettimeofday, so it will also reduce our chances of being hit by a change in the kernel ABI.

Targeting 1.9 because this seems too dangerous for 1.8.

aclements commented 8 years ago

I flipped through the other OSs. A few others use wall-clock time, as well:

ianlancetaylor commented 8 years ago

At least Solaris version 12 does have CLOCK_MONOTONIC. It is documented as CLOCK_HIGHRES, but CLOCK_MONOTONIC is defined in to the same value as CLOCK_HIGHRES and CLOCK_HIGHRES is defined by the man page to be monotonic.

namsral commented 7 years ago

FYI clock_gettime was introduced in September 2016 with the release of macOS 10.12 (Sierra)

minux commented 7 years ago

It will be some time before we can drop support for all pre-10.12 OS X versions, so I'm afraid we can't rely on 10.12-only syscalls now.

bradfitz commented 7 years ago

@minux, we can use it when available.

minux commented 7 years ago

But mach_absolute_time is available everywhere. The only problem is that I don't know if its Mach absolute time unit changes dynamically (in the lifetime of a process) or not.

note, AbsoluteToNanosecond is deprecated, but we can use absolutetime_to_nanosecond instead. However, we still need to figure out how to reimplement its functionality in Go. I'm hoping it doesn't require kernel specific memory accesses.

namsral commented 7 years ago

Using mach_timebase_info you can convert Mach absolute time to nanoseconds:

mach_timebase_info_data_t info;
mach_timebase_info(&info);

uint64_t start = mach_absolute_time();
usleep(2000000);
uint64_t duration = mach_absolute_time() - start;

// Convert to nanoseconds
duration = duration * info.numer / info.denom;

printf("%lld ns\n", (long long) duration);

From developer.apple.com

minux commented 7 years ago

mach_absolute_time is using rdtsc with some kernel provided magic numbers from comm page.

// from xnu-3248.60.11.1.2~2 _mach_absolute_time: 00007fff5fc25726 pushq %rbp 00007fff5fc25727 movq %rsp, %rbp 00007fff5fc2572a movabsq $0x7fffffe00050, %rsi ## imm = 0x7FFFFFE00050 00007fff5fc25734 movl 0x18(%rsi), %r8d 00007fff5fc25738 testl %r8d, %r8d 00007fff5fc2573b je 0x7fff5fc25734 00007fff5fc2573d lfence 00007fff5fc25740 rdtsc 00007fff5fc25742 lfence 00007fff5fc25745 shlq $0x20, %rdx 00007fff5fc25749 orq %rdx, %rax 00007fff5fc2574c movl 0xc(%rsi), %ecx 00007fff5fc2574f andl $0x1f, %ecx 00007fff5fc25752 subq (%rsi), %rax 00007fff5fc25755 shlq %cl, %rax 00007fff5fc25758 movl 0x8(%rsi), %ecx 00007fff5fc2575b mulq %rcx 00007fff5fc2575e shrdq $0x20, %rdx, %rax 00007fff5fc25763 addq 0x10(%rsi), %rax 00007fff5fc25767 cmpl 0x18(%rsi), %r8d 00007fff5fc2576b jne 0x7fff5fc25734 00007fff5fc2576d popq %rbp 00007fff5fc2576e retq 00007fff5fc2576f nop

I think it's the same as our current runtime.nanotime implementation.

namsral commented 7 years ago

My nanotime replacement to support monototic nanoseconds:

TEXT runtime·nanotime(SB), NOSPLIT, $32
    MOVQ $0x7fffffe00000, SI // comm page base

timeloop:
    MOVL  nt_generation(SI), R8
    TESTL R8, R8
    JZ    timeloop
    RDTSC
    SHLQ  $32, DX
    ORQ   DX, AX
    MOVL nt_shift(SI), CX
    SUBQ nt_tsc_base(SI), AX
    SHLQ CX, AX
    MOVL nt_scale(SI), CX
    MULQ CX
    SHRQ $32, AX:DX
    ADDQ nt_ns_base(SI), AX
    CMPL nt_generation(SI), R8
    JNE  timeloop
    MOVQ AX, ret+0(FP)
    RET

The algorithm is based on xnu's mach_absolute_time and works on Intel Macs.

My intention is to follow Go's contributions guidelines next.

gopherbot commented 7 years ago

CL https://golang.org/cl/35292 mentions this issue.