Arm64 atomic: add LSE atomics instructions to arm64

benchmark for the atomics instructions:
Without LSE instructions:
BenchmarkAndUint32-80                           138205774                8.562 ns/op
BenchmarkAndUint64-80                           139365404                8.571 ns/op
BenchmarkAndUint32Parallel-80                   16887577               259.6 ns/op
BenchmarkAndUint64Parallel-80                    6202482               281.2 ns/op
BenchmarkOrUint32-80                            138791865                8.592 ns/op
BenchmarkOrUint64-80                            138424888                8.670 ns/op
BenchmarkOrUint32Parallel-80                    15080332               175.1 ns/op
BenchmarkOrUint64Parallel-80                     8891142               262.2 ns/op
BenchmarkXorUint32-80                           139146337                8.611 ns/op
BenchmarkXorUint64-80                           138612325                8.627 ns/op
BenchmarkXorUint32Parallel-80                    3651159               548.5 ns/op
BenchmarkXorUint64Parallel-80                   17135235               350.2 ns/op
BenchmarkCompareAndSwapUint32-80                11712571                95.49 ns/op
BenchmarkCompareAndSwapUint64-80                12229885                87.32 ns/op
BenchmarkCompareAndSwapUint32Parallel-80        11853363               115.6 ns/op
BenchmarkCompareAndSwapUint64Parallel-80        15574476                87.91 ns/op

With LSE instructions:
BenchmarkAndUint32-80                           172129803                6.933 ns/op
BenchmarkAndUint64-80                           170516512                6.993 ns/op
BenchmarkAndUint32Parallel-80                   60089396                20.14 ns/op
BenchmarkAndUint64Parallel-80                   59266794                20.01 ns/op
BenchmarkOrUint32-80                            174805162                6.835 ns/op
BenchmarkOrUint64-80                            174935716                6.836 ns/op
BenchmarkOrUint32Parallel-80                    60084607                19.89 ns/op
BenchmarkOrUint64Parallel-80                    51555650                19.99 ns/op
BenchmarkXorUint32-80                           170525718                6.835 ns/op
BenchmarkXorUint64-80                           175269829                6.842 ns/op
BenchmarkXorUint32Parallel-80                   60005954                20.15 ns/op
BenchmarkXorUint64Parallel-80                   51590584                19.95 ns/op
BenchmarkCompareAndSwapUint32-80                27096822                40.69 ns/op
BenchmarkCompareAndSwapUint64-80                27516450                39.02 ns/op
BenchmarkCompareAndSwapUint32Parallel-80        27194458                40.69 ns/op
BenchmarkCompareAndSwapUint64Parallel-80        27614142                39.25 ns/op

Signed-off-by: howard zhang <howard.zhang@arm.com>
This commit is contained in:
howard zhang
2022-04-20 14:57:40 +08:00
committed by Howard Zhang
parent f4e537843f
commit 1cef45605e
5 changed files with 281 additions and 35 deletions
+13 -1
View File
@@ -10,14 +10,26 @@ go_library(
"aligned_32bit_unsafe.go",
"aligned_64bit.go",
"atomicbitops.go",
"atomicbitops_arm64.go",
"atomicbitops_amd64.s",
"atomicbitops_arm64.s",
"atomicbitops_noasm.go",
],
visibility = ["//:sandbox"],
deps = ["//pkg/sync"],
deps = [
"//pkg/cpuid",
"//pkg/sync",
],
)
go_test(
name = "atomicbitops_benchmark_test",
size = "small",
srcs = ["atomicbitops_benchmark_test.go"],
library = ":atomicbitops",
)
go_test(
name = "atomicbitops_test",
size = "small",
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2022 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.
//go:build arm64
// +build arm64
// Package atomicbitops provides extensions to the sync/atomic package.
//
// All read-modify-write operations implemented by this package have
// acquire-release memory ordering (like sync/atomic).
package atomicbitops
import "gvisor.dev/gvisor/pkg/cpuid"
var arm64HasATOMICS = cpuid.HostFeatureSet().HasFeature(cpuid.ARM64FeatureATOMICS)
+70 -34
View File
@@ -19,87 +19,123 @@
TEXT ·andUint32(SB),NOSPLIT,$0-12
MOVD ptr+0(FP), R0
MOVW val+8(FP), R1
again:
MOVBU ·arm64HasATOMICS(SB), R4
CBZ R4, load_store_loop
MVN R1, R2
LDCLRALW R2, (R0), R3
RET
load_store_loop:
LDAXRW (R0), R2
ANDW R1, R2
STLXRW R2, (R0), R3
CBNZ R3, again
CBNZ R3, load_store_loop
RET
TEXT ·orUint32(SB),NOSPLIT,$0-12
MOVD ptr+0(FP), R0
MOVW val+8(FP), R1
again:
MOVBU ·arm64HasATOMICS(SB), R4
CBZ R4, load_store_loop
LDORALW R1, (R0), R2
RET
load_store_loop:
LDAXRW (R0), R2
ORRW R1, R2
STLXRW R2, (R0), R3
CBNZ R3, again
CBNZ R3, load_store_loop
RET
TEXT ·xorUint32(SB),NOSPLIT,$0-12
MOVD ptr+0(FP), R0
MOVW val+8(FP), R1
again:
MOVBU ·arm64HasATOMICS(SB), R4
CBZ R4, load_store_loop
LDEORALW R1, (R0), R2
RET
load_store_loop:
LDAXRW (R0), R2
EORW R1, R2
STLXRW R2, (R0), R3
CBNZ R3, again
CBNZ R3, load_store_loop
RET
TEXT ·compareAndSwapUint32(SB),NOSPLIT,$0-20
MOVD addr+0(FP), R0
MOVW old+8(FP), R1
MOVW new+12(FP), R2
again:
LDAXRW (R0), R3
CMPW R1, R3
BNE done
STLXRW R2, (R0), R4
CBNZ R4, again
done:
MOVW R3, prev+16(FP)
MOVD addr+0(FP), R0
MOVW old+8(FP), R1
MOVW new+12(FP), R2
MOVBU ·arm64HasATOMICS(SB), R4
CBZ R4, load_store_loop
CASALW R1, (R0), R2
MOVW R1, prev+16(FP)
RET
load_store_loop:
LDAXRW (R0), R3
CMPW R1, R3
BNE ok
STLXRW R2, (R0), R4
CBNZ R4, load_store_loop
ok:
MOVW R3, prev+16(FP)
RET
TEXT ·andUint64(SB),NOSPLIT,$0-16
MOVD ptr+0(FP), R0
MOVD val+8(FP), R1
again:
MOVBU ·arm64HasATOMICS(SB), R4
CBZ R4, load_store_loop
MVN R1, R2
LDCLRALD R2, (R0), R3
RET
load_store_loop:
LDAXR (R0), R2
AND R1, R2
STLXR R2, (R0), R3
CBNZ R3, again
CBNZ R3, load_store_loop
RET
TEXT ·orUint64(SB),NOSPLIT,$0-16
MOVD ptr+0(FP), R0
MOVD val+8(FP), R1
again:
MOVBU ·arm64HasATOMICS(SB), R4
CBZ R4, load_store_loop
LDORALD R1, (R0), R2
RET
load_store_loop:
LDAXR (R0), R2
ORR R1, R2
STLXR R2, (R0), R3
CBNZ R3, again
CBNZ R3, load_store_loop
RET
TEXT ·xorUint64(SB),NOSPLIT,$0-16
MOVD ptr+0(FP), R0
MOVD val+8(FP), R1
again:
MOVBU ·arm64HasATOMICS(SB), R4
CBZ R4, load_store_loop
LDEORALD R1, (R0), R2
RET
load_store_loop:
LDAXR (R0), R2
EOR R1, R2
STLXR R2, (R0), R3
CBNZ R3, again
CBNZ R3, load_store_loop
RET
TEXT ·compareAndSwapUint64(SB),NOSPLIT,$0-32
MOVD addr+0(FP), R0
MOVD old+8(FP), R1
MOVD new+16(FP), R2
again:
LDAXR (R0), R3
CMP R1, R3
BNE done
STLXR R2, (R0), R4
CBNZ R4, again
done:
MOVD R3, prev+24(FP)
MOVD addr+0(FP), R0
MOVD old+8(FP), R1
MOVD new+16(FP), R2
MOVBU ·arm64HasATOMICS(SB), R4
CBZ R4, load_store_loop
CASALD R1, (R0), R2
MOVD R1, prev+24(FP)
RET
load_store_loop:
LDAXR (R0), R3
CMP R1, R3
BNE ok
STLXR R2, (R0), R4
CBNZ R4, load_store_loop
ok:
MOVD R3, prev+24(FP)
RET
@@ -0,0 +1,171 @@
// 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 atomicbitops
import (
"testing"
)
func BenchmarkAndUint32(b *testing.B) {
val32 := FromUint32(0xfffffff)
for i := 0; i < b.N; i++ {
AndUint32(&val32, uint32(i))
}
}
func BenchmarkAndUint64(b *testing.B) {
val64 := FromUint64(0xfffffffffffffff)
for i := 0; i < b.N; i++ {
AndUint64(&val64, uint64(i))
}
}
func BenchmarkAndUint32Parallel(b *testing.B) {
val32 := FromUint32(0xfffffff)
b.RunParallel(func(pb *testing.PB) {
i := uint32(0)
for pb.Next() {
AndUint32(&val32, i)
i++
}
})
}
func BenchmarkAndUint64Parallel(b *testing.B) {
val64 := FromUint64(0xfffffffffffffff)
b.RunParallel(func(pb *testing.PB) {
i := uint64(0)
for pb.Next() {
AndUint64(&val64, i)
i++
}
})
}
func BenchmarkOrUint32(b *testing.B) {
val32 := FromUint32(0xfffffff)
for i := 0; i < b.N; i++ {
OrUint32(&val32, uint32(i))
}
}
func BenchmarkOrUint64(b *testing.B) {
val64 := FromUint64(0xfffffffffffffff)
for i := 0; i < b.N; i++ {
OrUint64(&val64, uint64(i))
}
}
func BenchmarkOrUint32Parallel(b *testing.B) {
val32 := FromUint32(0xfffffff)
b.RunParallel(func(pb *testing.PB) {
i := uint32(0)
for pb.Next() {
OrUint32(&val32, i)
i++
}
})
}
func BenchmarkOrUint64Parallel(b *testing.B) {
val64 := FromUint64(0xfffffffffffffff)
b.RunParallel(func(pb *testing.PB) {
i := uint64(0)
for pb.Next() {
OrUint64(&val64, i)
i++
}
})
}
func BenchmarkXorUint32(b *testing.B) {
val32 := FromUint32(0xfffffff)
for i := 0; i < b.N; i++ {
XorUint32(&val32, uint32(i))
}
}
func BenchmarkXorUint64(b *testing.B) {
val64 := FromUint64(0xfffffffffffffff)
for i := 0; i < b.N; i++ {
XorUint64(&val64, uint64(i))
}
}
func BenchmarkXorUint32Parallel(b *testing.B) {
val32 := FromUint32(0xfffffff)
b.RunParallel(func(pb *testing.PB) {
i := uint32(0)
for pb.Next() {
XorUint32(&val32, i)
i++
}
})
}
func BenchmarkXorUint64Parallel(b *testing.B) {
val64 := FromUint64(0xfffffffffffffff)
b.RunParallel(func(pb *testing.PB) {
i := uint64(0)
for pb.Next() {
XorUint64(&val64, i)
i++
}
})
}
func BenchmarkCompareAndSwapUint32(b *testing.B) {
x := FromUint32(1)
ptr := &x
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
CompareAndSwapUint32(ptr, 1, 0)
CompareAndSwapUint32(ptr, 0, 1)
}
})
}
func BenchmarkCompareAndSwapUint64(b *testing.B) {
x := FromUint64(1)
ptr := &x
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
CompareAndSwapUint64(ptr, 1, 0)
CompareAndSwapUint64(ptr, 0, 1)
}
})
}
func BenchmarkCompareAndSwapUint32Parallel(b *testing.B) {
x := FromUint32(1)
ptr := &x
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
CompareAndSwapUint32(ptr, 1, 0)
CompareAndSwapUint32(ptr, 0, 1)
}
})
}
func BenchmarkCompareAndSwapUint64Parallel(b *testing.B) {
x := FromUint64(1)
ptr := &x
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
CompareAndSwapUint64(ptr, 1, 0)
CompareAndSwapUint64(ptr, 0, 1)
}
})
}
+1
View File
@@ -50,6 +50,7 @@ deps_test(
"//pkg/atomicbitops",
"//pkg/buffer",
"//pkg/context",
"//pkg/cpuid",
"//pkg/gohacks",
"//pkg/goid",
"//pkg/ilist",