Skip to content

Commit a946c00

Browse files
committed
Refactor, speed.
1 parent 3215376 commit a946c00

File tree

3 files changed

+94
-149
lines changed

3 files changed

+94
-149
lines changed

vfs/shm_copy.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//go:build (windows && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys) || sqlite3_dotlk
2+
3+
package vfs
4+
5+
import (
6+
"unsafe"
7+
8+
"github.com/ncruces/go-sqlite3/internal/util"
9+
)
10+
11+
const (
12+
_WALINDEX_HDR_SIZE = 136
13+
_WALINDEX_PGSZ = 32768
14+
)
15+
16+
// This looks like a safe way of keeping the WAL-index in sync.
17+
//
18+
// The WAL-index file starts with a header,
19+
// and the index doesn't meaningfully change if the header doesn't change.
20+
//
21+
// The header starts with two 48 byte, checksummed, copies of the same information,
22+
// which are accessed independently between memory barriers.
23+
// The checkpoint information that follows uses 4 byte aligned words.
24+
//
25+
// Finally, we have the WAL-index hash tables,
26+
// which are only modified holding the exclusive WAL_WRITE_LOCK.
27+
//
28+
// Since all the data is either redundant+checksummed,
29+
// 4 byte aligned, or modified under an exclusive lock,
30+
// the copies below should correctly keep copies in sync.
31+
//
32+
// https://sqlite.org/walformat.html#the_wal_index_file_format
33+
34+
func (s *vfsShm) shmAcquire() {
35+
if len(s.ptrs) == 0 || shmUnmodified(s.shadow[0][:], s.shared[0][:]) {
36+
return
37+
}
38+
// Copies modified words from shared to private memory.
39+
for id, p := range s.ptrs {
40+
shared := shmPage(s.shared[id][:])
41+
shadow := shmPage(s.shadow[id][:])
42+
privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
43+
for i, shared := range shared {
44+
if shadow[i] != shared {
45+
shadow[i] = shared
46+
privat[i] = shared
47+
}
48+
}
49+
}
50+
}
51+
52+
func (s *vfsShm) shmRelease() {
53+
if len(s.ptrs) == 0 || shmUnmodified(s.shadow[0][:], util.View(s.mod, s.ptrs[0], _WALINDEX_HDR_SIZE)) {
54+
return
55+
}
56+
// Copies modified words from private to shared memory.
57+
for id, p := range s.ptrs {
58+
shared := shmPage(s.shared[id][:])
59+
shadow := shmPage(s.shadow[id][:])
60+
privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
61+
for i, privat := range privat {
62+
if shadow[i] != privat {
63+
shadow[i] = privat
64+
shared[i] = privat
65+
}
66+
}
67+
}
68+
}
69+
70+
func (s *vfsShm) shmBarrier() {
71+
s.Lock()
72+
s.shmAcquire()
73+
s.shmRelease()
74+
s.Unlock()
75+
}
76+
77+
func shmPage(s []byte) *[_WALINDEX_PGSZ / 4]uint32 {
78+
p := (*uint32)(unsafe.Pointer(unsafe.SliceData(s)))
79+
return (*[_WALINDEX_PGSZ / 4]uint32)(unsafe.Slice(p, _WALINDEX_PGSZ/4))
80+
}
81+
82+
func shmUnmodified(v1, v2 []byte) bool {
83+
return *(*[_WALINDEX_HDR_SIZE]byte)(v1[:]) == *(*[_WALINDEX_HDR_SIZE]byte)(v2[:])
84+
}

vfs/shm_dotlk.go

Lines changed: 7 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@ import (
88
"io/fs"
99
"os"
1010
"sync"
11-
"unsafe"
1211

1312
"github.com/ncruces/go-sqlite3/internal/util"
1413
"github.com/tetratelabs/wazero/api"
1514
)
1615

1716
type vfsShmBuffer struct {
18-
shared []byte // +checklocks:Mutex
19-
refs int // +checklocks:vfsShmBuffersMtx
17+
shared [][_WALINDEX_PGSZ]byte
18+
refs int // +checklocks:vfsShmBuffersMtx
2019

2120
lock [_SHM_NLOCK]int16 // +checklocks:Mutex
2221
sync.Mutex
@@ -34,7 +33,7 @@ type vfsShm struct {
3433
alloc api.Function
3534
free api.Function
3635
path string
37-
shadow []byte
36+
shadow [][_WALINDEX_PGSZ]byte
3837
ptrs []uint32
3938
stack [1]uint64
4039
lock [_SHM_NLOCK]bool
@@ -115,17 +114,15 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
115114
defer s.Unlock()
116115
defer s.shmAcquire()
117116

118-
n := (int(id) + 1) * int(size)
119-
120-
if n > len(s.shared) {
117+
if int(id) >= len(s.shared) {
121118
if !extend {
122119
return 0, _OK
123120
}
124-
s.shared = append(s.shared, make([]byte, n-len(s.shared))...)
121+
s.shared = append(s.shared, make([][_WALINDEX_PGSZ]byte, int(id)-len(s.shared)+1)...)
125122
}
126123

127-
if n > len(s.shadow) {
128-
s.shadow = append(s.shadow, make([]byte, n-len(s.shadow))...)
124+
if int(id) >= len(s.shadow) {
125+
s.shadow = append(s.shadow, make([][_WALINDEX_PGSZ]byte, int(id)-len(s.shadow)+1)...)
129126
}
130127

131128
for int(id) >= len(s.ptrs) {
@@ -221,80 +218,3 @@ func (s *vfsShm) shmUnmap(delete bool) {
221218
s.ptrs = nil
222219
s.shadow = nil
223220
}
224-
225-
func (s *vfsShm) shmBarrier() {
226-
s.Lock()
227-
s.shmAcquire()
228-
s.shmRelease()
229-
s.Unlock()
230-
}
231-
232-
// This looks like a safe, if inefficient, way of keeping memory in sync.
233-
//
234-
// The WAL-index file starts with a header.
235-
// This header starts with two 48 byte, checksummed, copies of the same information,
236-
// which are accessed independently between memory barriers.
237-
// The checkpoint information that follows uses 4 byte aligned words.
238-
//
239-
// Finally, we have the WAL-index hash tables,
240-
// which are only modified holding the exclusive WAL_WRITE_LOCK.
241-
// Also, aHash isn't modified unless aPgno changes.
242-
//
243-
// Since all the data is either redundant+checksummed,
244-
// 4 byte aligned, or modified under an exclusive lock,
245-
// the copies below should correctly keep memory in sync.
246-
//
247-
// https://sqlite.org/walformat.html#the_wal_index_file_format
248-
249-
const _WALINDEX_PGSZ = 32768
250-
251-
// +checklocks:s.Mutex
252-
func (s *vfsShm) shmAcquire() {
253-
// Copies modified words from shared to private memory.
254-
for id, p := range s.ptrs {
255-
i0 := id * _WALINDEX_PGSZ
256-
i1 := i0 + _WALINDEX_PGSZ
257-
shared := shmPage(s.shared[i0:i1])
258-
shadow := shmPage(s.shadow[i0:i1])
259-
privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
260-
if shmPageEq(shadow, shared) {
261-
continue
262-
}
263-
for i, shared := range shared {
264-
if shadow[i] != shared {
265-
shadow[i] = shared
266-
privat[i] = shared
267-
}
268-
}
269-
}
270-
}
271-
272-
// +checklocks:s.Mutex
273-
func (s *vfsShm) shmRelease() {
274-
// Copies modified words from private to shared memory.
275-
for id, p := range s.ptrs {
276-
i0 := id * _WALINDEX_PGSZ
277-
i1 := i0 + _WALINDEX_PGSZ
278-
shared := shmPage(s.shared[i0:i1])
279-
shadow := shmPage(s.shadow[i0:i1])
280-
privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
281-
if shmPageEq(shadow, privat) {
282-
continue
283-
}
284-
for i, privat := range privat {
285-
if shadow[i] != privat {
286-
shadow[i] = privat
287-
shared[i] = privat
288-
}
289-
}
290-
}
291-
}
292-
293-
func shmPage(s []byte) *[_WALINDEX_PGSZ / 4]uint32 {
294-
p := (*uint32)(unsafe.Pointer(unsafe.SliceData(s)))
295-
return (*[_WALINDEX_PGSZ / 4]uint32)(unsafe.Slice(p, _WALINDEX_PGSZ/4))
296-
}
297-
298-
func shmPageEq(p1, p2 *[_WALINDEX_PGSZ / 4]uint32) bool {
299-
return *(*[_WALINDEX_PGSZ / 8]uint32)(p1[:]) == *(*[_WALINDEX_PGSZ / 8]uint32)(p2[:])
300-
}

vfs/shm_windows.go

Lines changed: 3 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"os"
99
"sync"
1010
"time"
11-
"unsafe"
1211

1312
"github.com/tetratelabs/wazero/api"
1413
"golang.org/x/sys/windows"
@@ -25,7 +24,7 @@ type vfsShm struct {
2524
path string
2625
regions []*util.MappedRegion
2726
shared [][]byte
28-
shadow []byte
27+
shadow [][_WALINDEX_PGSZ]byte
2928
ptrs []uint32
3029
stack [1]uint64
3130
blocking bool
@@ -108,8 +107,8 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
108107
s.shared[id] = r.Data
109108

110109
// Allocate shadow memory.
111-
if n := (int(id) + 1) * int(size); n > len(s.shadow) {
112-
s.shadow = append(s.shadow, make([]byte, n-len(s.shadow))...)
110+
if int(id) >= len(s.shadow) {
111+
s.shadow = append(s.shadow, make([][_WALINDEX_PGSZ]byte, int(id)-len(s.shadow)+1)...)
113112
}
114113

115114
// Allocate local memory.
@@ -179,64 +178,6 @@ func (s *vfsShm) shmUnmap(delete bool) {
179178
}
180179
}
181180

182-
func (s *vfsShm) shmBarrier() {
183-
s.Lock()
184-
s.shmAcquire()
185-
s.shmRelease()
186-
s.Unlock()
187-
}
188-
189-
const _WALINDEX_PGSZ = 32768
190-
191-
func (s *vfsShm) shmAcquire() {
192-
// Copies modified words from shared to private memory.
193-
for id, p := range s.ptrs {
194-
i0 := id * _WALINDEX_PGSZ
195-
i1 := i0 + _WALINDEX_PGSZ
196-
shared := shmPage(s.shared[id])
197-
shadow := shmPage(s.shadow[i0:i1])
198-
privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
199-
if shmPageEq(shadow, shared) {
200-
continue
201-
}
202-
for i, shared := range shared {
203-
if shadow[i] != shared {
204-
shadow[i] = shared
205-
privat[i] = shared
206-
}
207-
}
208-
}
209-
}
210-
211-
func (s *vfsShm) shmRelease() {
212-
// Copies modified words from private to shared memory.
213-
for id, p := range s.ptrs {
214-
i0 := id * _WALINDEX_PGSZ
215-
i1 := i0 + _WALINDEX_PGSZ
216-
shared := shmPage(s.shared[id])
217-
shadow := shmPage(s.shadow[i0:i1])
218-
privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
219-
if shmPageEq(shadow, privat) {
220-
continue
221-
}
222-
for i, privat := range privat {
223-
if shadow[i] != privat {
224-
shadow[i] = privat
225-
shared[i] = privat
226-
}
227-
}
228-
}
229-
}
230-
231-
func shmPage(s []byte) *[_WALINDEX_PGSZ / 4]uint32 {
232-
p := (*uint32)(unsafe.Pointer(unsafe.SliceData(s)))
233-
return (*[_WALINDEX_PGSZ / 4]uint32)(unsafe.Slice(p, _WALINDEX_PGSZ/4))
234-
}
235-
236-
func shmPageEq(p1, p2 *[_WALINDEX_PGSZ / 4]uint32) bool {
237-
return *(*[_WALINDEX_PGSZ / 8]uint32)(p1[:]) == *(*[_WALINDEX_PGSZ / 8]uint32)(p2[:])
238-
}
239-
240181
func (s *vfsShm) shmEnableBlocking(block bool) {
241182
s.blocking = block
242183
}

0 commit comments

Comments
 (0)