Use vDSO clock_gettime

The vDSO calls are much faster.

For exeample, here are results of read_benchmark_test_tmpfs_kvm before and
after this change:
Before:
BM_Read/65536/real_time  7309 ns 7260 ns  82640 bytes_per_second=8.35016G/s
After:
BM_Read/65536/real_time  2065 ns 2080 ns 346151 bytes_per_second=29.558G/s
PiperOrigin-RevId: 409547486
This commit is contained in:
Andrei Vagin
2021-11-12 17:33:28 -08:00
committed by gVisor bot
parent b490036d83
commit ea03144651
7 changed files with 173 additions and 58 deletions
+8 -1
View File
@@ -27,10 +27,12 @@ go_library(
"sampler.go",
"sampler_amd64.go",
"sampler_arm64.go",
"sampler_unsafe.go",
"seqatomic_parameters_unsafe.go",
"tsc_amd64.s",
"tsc_arm64.s",
"vdso.go",
"vdso_amd64.s",
"vdso_arm64.s",
],
visibility = ["//:sandbox"],
deps = [
@@ -49,6 +51,11 @@ go_test(
"calibrated_clock_test.go",
"parameters_test.go",
"sampler_test.go",
"vdso_test.go",
],
library = ":time",
deps = [
"//pkg/abi/linux",
"@org_golang_x_sys//unix:go_default_library",
],
)
+33
View File
@@ -17,6 +17,7 @@ package time
import (
"errors"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/log"
)
@@ -216,3 +217,35 @@ func (s *sampler) Range() (sample, sample, bool) {
return s.samples[0], s.samples[len(s.samples)-1], true
}
// syscallTSCReferenceClocks is the standard referenceClocks, collecting
// samples using CLOCK_GETTIME and RDTSC.
type syscallTSCReferenceClocks struct {
tscCycleClock
}
// Sample implements sampler.Sample.
func (syscallTSCReferenceClocks) Sample(c ClockID) (sample, error) {
var s sample
s.before = Rdtsc()
// Don't call clockGettime to avoid a call which may call morestack.
var ts unix.Timespec
vdsoClockGettime(c, &ts)
s.after = Rdtsc()
s.ref = ReferenceNS(ts.Nano())
return s, nil
}
// clockGettime calls SYS_CLOCK_GETTIME, returning time in nanoseconds.
func clockGettime(c ClockID) (ReferenceNS, error) {
var ts unix.Timespec
vdsoClockGettime(c, &ts)
return ReferenceNS(ts.Nano()), nil
}
-57
View File
@@ -1,57 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package time
import (
"unsafe"
"golang.org/x/sys/unix"
)
// syscallTSCReferenceClocks is the standard referenceClocks, collecting
// samples using CLOCK_GETTIME and RDTSC.
type syscallTSCReferenceClocks struct {
tscCycleClock
}
// Sample implements sampler.Sample.
func (syscallTSCReferenceClocks) Sample(c ClockID) (sample, error) {
var s sample
s.before = Rdtsc()
// Don't call clockGettime to avoid a call which may call morestack.
var ts unix.Timespec
_, _, e := unix.RawSyscall(unix.SYS_CLOCK_GETTIME, uintptr(c), uintptr(unsafe.Pointer(&ts)), 0)
if e != 0 {
return sample{}, e
}
s.after = Rdtsc()
s.ref = ReferenceNS(ts.Nano())
return s, nil
}
// clockGettime calls SYS_CLOCK_GETTIME, returning time in nanoseconds.
func clockGettime(c ClockID) (ReferenceNS, error) {
var ts unix.Timespec
_, _, e := unix.RawSyscall(unix.SYS_CLOCK_GETTIME, uintptr(c), uintptr(unsafe.Pointer(&ts)), 0)
if e != 0 {
return 0, e
}
return ReferenceNS(ts.Nano()), nil
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package time
import (
"golang.org/x/sys/unix"
)
func vdsoClockGettime(clockid ClockID, ts *unix.Timespec) int
+32
View File
@@ -0,0 +1,32 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "textflag.h"
#define SYS_clock_gettime 228
TEXT ·vdsoClockGettime(SB), NOSPLIT, $0-24
MOVL clockid+0(FP), DI
MOVQ ts+8(FP), SI
MOVQ runtime·vdsoClockgettimeSym(SB), AX
CMPQ AX, $0
JEQ fallback
CALL AX
MOVQ AX, ret+16(FP)
RET
fallback:
MOVQ $SYS_clock_gettime, AX
SYSCALL
MOVQ AX, ret+16(FP)
RET
+31
View File
@@ -0,0 +1,31 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "textflag.h"
#define SYS_clock_gettime 113
TEXT ·vdsoClockGettime(SB), NOSPLIT, $0-24
MOVW clockid+0(FP), R0
MOVD ts+8(FP), R1
MOVD runtime·vdsoClockgettimeSym(SB), R2
CBZ R2, fallback
BL (R2)
MOVD R0, ret+16(FP)
RET
fallback:
MOVD $SYS_clock_gettime, R8
SVC
MOVD R0, ret+16(FP)
RET
+48
View File
@@ -0,0 +1,48 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package time
import (
"syscall"
"testing"
"unsafe"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
)
func TestClockGetTime(t *testing.T) {
ts := unix.Timespec{}
if ret := vdsoClockGettime(linux.CLOCK_MONOTONIC, &ts); ret != 0 {
t.Fatalf("Unexpected error code: %v", ret)
}
sts := unix.Timespec{}
if _, _, errno := unix.RawSyscall(unix.SYS_CLOCK_GETTIME,
uintptr(linux.CLOCK_MONOTONIC),
uintptr(unsafe.Pointer(&sts)), 0); errno != 0 {
t.Fatalf("Unexpected error code: %v", errno)
}
// Check that ts.Sec is in [sts.Sec, sts.Sec + 5].
if sts.Sec < ts.Sec || sts.Sec > ts.Sec+5 {
t.Fatalf("Unexpected delta: vdso %+v syscall %+v", ts, sts)
}
}
func TestClockGetTimeEINVAL(t *testing.T) {
ts := unix.Timespec{}
if ret := vdsoClockGettime(-1, &ts); ret != -int(syscall.EINVAL) {
t.Fatalf("Unexpected error code: %v", ret)
}
}