-
Notifications
You must be signed in to change notification settings - Fork 0
/
fastcopy.go
110 lines (99 loc) · 2.29 KB
/
fastcopy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package assembly
import (
"unsafe"
"github.com/klauspost/cpuid/v2"
"golang.org/x/exp/constraints"
)
// Experimental, high risk and unsafe.
// Not recommended to use in production.
var (
// In debugging mode, set it as false
hasERMS = cpuid.CPU.Has(cpuid.ERMS)
isX86_64 = cpuid.CPU.X64Level() != 0
)
type CanMove interface {
constraints.Integer |
constraints.Float |
constraints.Complex |
~string |
uintptr
}
//go:linkname memmove runtime.memmove
func memmove(to, from unsafe.Pointer, n uintptr)
// Assembly definitions mapping function body automatically.
func can_movs(to, from unsafe.Pointer, n uintptr) (ok bool)
func copy_movsb(to, from unsafe.Pointer, n uintptr)
func copy_movsq(to, from unsafe.Pointer, n uintptr) (left, copied uintptr)
func FastCopy[T CanMove](dst, src []T) (n int) {
n = min(len(src), len(dst))
if n <= 0 {
return
}
pDst := unsafe.Pointer(&dst[0])
pSrc := unsafe.Pointer(&src[0])
if pDst == pSrc {
n = 0
return
}
pN := uintptr(n)
elementSize := unsafe.Sizeof(src[0])
size := pN * elementSize
if size > 15500 && (hasERMS || isX86_64) && can_movs(pDst, pSrc, pN) {
if hasERMS {
copy_movsb(pDst, pSrc, size)
} else {
left, copied := copy_movsq(pDst, pSrc, size)
if left > 0 {
memmove(unsafe.Pointer(&dst[copied]), unsafe.Pointer(&src[copied]), left*elementSize)
}
}
} else {
memmove(pDst, pSrc, size)
}
return
}
func FastCopyByMOVSB[T CanMove](dst, src []T) (n int) {
n = min(len(src), len(dst))
if n <= 0 {
return
}
pDst := unsafe.Pointer(&dst[0])
pSrc := unsafe.Pointer(&src[0])
if pDst == pSrc {
n = 0
return
}
pN := uintptr(n)
elementSize := unsafe.Sizeof(src[0])
size := pN * elementSize
if can_movs(pDst, pSrc, pN) {
copy_movsb(pDst, pSrc, size)
} else {
memmove(pDst, pSrc, size)
}
return
}
func FastCopyByMOVSQ[T CanMove](dst, src []T) (n int) {
n = min(len(src), len(dst))
if n <= 0 {
return
}
pDst := unsafe.Pointer(&dst[0])
pSrc := unsafe.Pointer(&src[0])
if pDst == pSrc {
n = 0
return
}
pN := uintptr(n)
elementSize := unsafe.Sizeof(src[0])
size := pN * elementSize
if can_movs(pDst, pSrc, pN) {
left, copied := copy_movsq(pDst, pSrc, size)
if left > 0 {
memmove(unsafe.Pointer(&dst[copied]), unsafe.Pointer(&src[copied]), left*elementSize)
}
} else {
memmove(pDst, pSrc, size)
}
return
}