Skip to content

Commit 52cf25b

Browse files
committed
Revise for better performance.
Replace contracts with inline checks and unsafe operations for gvector-ref, gvector-set!, gvector-add!, and in-gvector iteration. Use vec-before-n read ordering for memory safety under concurrent access. Add parallel/concurrent stress tests.
1 parent f6d64c1 commit 52cf25b

3 files changed

Lines changed: 422 additions & 90 deletions

File tree

data-lib/data/gvector.rkt

Lines changed: 141 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,23 @@
33
(require (for-syntax racket/base
44
syntax/contract
55
syntax/for-body)
6+
racket/performance-hint
67
racket/serialize
8+
racket/fixnum
79
racket/contract/base
810
racket/dict
11+
racket/unsafe/ops
912
racket/vector
1013
racket/struct)
1114

1215
(define DEFAULT-CAPACITY 10)
1316

17+
(define MIN-CAPACITY 8)
18+
1419
(define (make-gvector #:capacity [capacity DEFAULT-CAPACITY])
15-
(gvector (make-vector capacity #f) 0))
20+
(unless (exact-nonnegative-integer? capacity)
21+
(raise-argument-error* 'make-gvector 'data/gvector "exact-nonnegative-integer?" capacity))
22+
(gvector (make-vector (max capacity MIN-CAPACITY) 0) 0))
1623

1724
(define gvector*
1825
(let ([gvector
@@ -29,43 +36,62 @@
2936
(unless (< index hi)
3037
(raise-range-error who "gvector" "" index gv 0 (sub1 hi))))
3138

32-
;; ensure-free-space! : GVector Nat -> Void
33-
(define (ensure-free-space! gv needed-free-space)
34-
(define vec (gvector-vec gv))
35-
(define n (gvector-n gv))
36-
(define cap (vector-length vec))
37-
(define needed-cap (+ n needed-free-space))
38-
(unless (<= needed-cap cap)
39-
(define new-cap
40-
(let loop ([new-cap (max DEFAULT-CAPACITY cap)])
41-
(if (<= needed-cap new-cap) new-cap (loop (* 2 new-cap)))))
42-
(define new-vec (make-vector new-cap #f))
43-
(vector-copy! new-vec 0 vec)
44-
(set-gvector-vec! gv new-vec)))
45-
46-
(define gvector-add!
47-
(case-lambda
48-
[(gv item)
49-
(ensure-free-space! gv 1)
50-
(define n (gvector-n gv))
51-
(define v (gvector-vec gv))
52-
(vector-set! v n item)
53-
(set-gvector-n! gv (add1 n))]
54-
[(gv . items)
55-
(define item-count (length items))
56-
(ensure-free-space! gv item-count)
57-
(define n (gvector-n gv))
58-
(define v (gvector-vec gv))
59-
(for ([index (in-naturals n)] [item (in-list items)])
60-
(vector-set! v index item))
61-
(set-gvector-n! gv (+ n item-count))]))
39+
(begin-encourage-inline
40+
41+
(define (check-gvector who gv)
42+
(unless (gvector? gv)
43+
(raise-argument-error* who 'data/gvector "gvector?" gv)))
44+
45+
46+
;; ensure-free-space-vec! : Vector Nat Nat -> Vector/#f
47+
(define (ensure-free-space-vec! vec n needed-free-space)
48+
(define cap (unsafe-vector*-length vec))
49+
(define needed-cap (unsafe-fx+ n needed-free-space))
50+
(cond [(<= needed-cap cap) #f]
51+
[else
52+
;; taken from Rust's raw_vec implementation
53+
(let* ([new-cap (unsafe-fxmax (unsafe-fx* 2 cap) needed-cap)]
54+
[new-cap (unsafe-fxmax new-cap MIN-CAPACITY)])
55+
(vector*-extend vec new-cap 0))]))
56+
57+
(define (ensure-free-space! gv needed-free-space)
58+
(define v (ensure-free-space-vec! (gvector-vec gv) (gvector-n gv) needed-free-space))
59+
(when v (set-gvector-vec! gv v)))
60+
61+
(define-syntax-rule (define/ensure-space! (n v) gv needed-free-space)
62+
(begin (define n (gvector-n gv))
63+
(define v1 (gvector-vec gv))
64+
(define v2 (ensure-free-space-vec! v1 n needed-free-space))
65+
(define v (if v2 (begin (set-gvector-vec! gv v2) v2) v1))))
66+
67+
;; only safe on unchaperoned gvectors
68+
(define (unsafe-gvector-add! gv item)
69+
(define/ensure-space! (n v) gv 1)
70+
(unsafe-vector*-set! v n item)
71+
(set-gvector-n! gv (unsafe-fx+ 1 n)))
72+
73+
(define gvector-add!
74+
(case-lambda
75+
[(gv item)
76+
(check-gvector 'gvector-add! gv)
77+
(define/ensure-space! (n v) gv 1)
78+
(unsafe-vector*-set! v n item)
79+
(set-gvector-n! gv (unsafe-fx+ 1 n))]
80+
[(gv . items)
81+
(check-gvector 'gvector-add! gv)
82+
(define item-count (length items))
83+
(define/ensure-space! (n v) gv item-count)
84+
(for ([index (in-naturals n)] [item (in-list items)])
85+
(unsafe-vector*-set! v index item))
86+
(set-gvector-n! gv (+ n item-count))])))
6287

6388
;; SLOW!
6489
(define (gvector-insert! gv index item)
6590
;; This does (n - index) redundant copies on resize, but that
6691
;; happens rarely and I prefer the simpler code.
67-
(define n (gvector-n gv))
92+
(check-gvector 'gvector-insert! gv)
6893
(check-index 'gvector-insert! gv index #t)
94+
(define n (gvector-n gv))
6995
(ensure-free-space! gv 1)
7096
(define v (gvector-vec gv))
7197
(vector-copy! v (add1 index) v index n)
@@ -97,6 +123,7 @@
97123

98124
;; SLOW!
99125
(define (gvector-remove! gv index)
126+
(check-gvector 'gvector-remove! gv)
100127
(define n (gvector-n gv))
101128
(define v (gvector-vec gv))
102129
(check-index 'gvector-remove! gv index #f)
@@ -106,6 +133,7 @@
106133
(trim! gv))
107134

108135
(define (gvector-remove-last! gv)
136+
(check-gvector 'gvector-remove-last! gv)
109137
(let ([n (gvector-n gv)]
110138
[v (gvector-vec gv)])
111139
(unless (> n 0) (error 'gvector-remove-last! "empty"))
@@ -114,45 +142,74 @@
114142
last-val))
115143

116144
(define (gvector-count gv)
145+
(check-gvector 'gvector-count gv)
117146
(gvector-n gv))
118147

119148
(define none (gensym 'none))
120149

121150
(define (gvector-ref gv index [default none])
151+
(check-gvector 'gvector-ref gv)
122152
(unless (exact-nonnegative-integer? index)
123153
(raise-type-error 'gvector-ref "exact nonnegative integer" index))
124-
(if (< index (gvector-n gv))
125-
(vector-ref (gvector-vec gv) index)
126-
(cond [(eq? default none)
127-
(check-index 'gvector-ref gv index #f)]
128-
[(procedure? default) (default)]
129-
[else default])))
154+
(let ([v (gvector-vec gv)])
155+
(if (< index (gvector-n gv))
156+
(unsafe-vector*-ref v index)
157+
(cond [(eq? default none)
158+
(check-index 'gvector-ref gv index #f)]
159+
[(procedure? default) (default)]
160+
[else default]))))
161+
162+
(define (gvector-append! gv gv*)
163+
(check-gvector 'gvector-append! gv)
164+
(check-gvector 'gvector-append! gv*)
165+
(ensure-free-space! gv (gvector-n gv*))
166+
(vector-copy! (gvector-vec gv) (gvector-n gv) (gvector-vec gv*)))
167+
168+
(define (gvector-append gv gv*)
169+
(check-gvector 'gvector-append gv)
170+
(check-gvector 'gvector-append gv*)
171+
;; retain the spare capacity of gv*
172+
(define gv0 (make-gvector #:capacity (+ (gvector-n gv) (vector-length (gvector-vec gv*)))))
173+
(define v0 (gvector-vec gv0))
174+
(vector-copy! v0 0 (gvector-vec gv) 0 (gvector-n gv))
175+
(vector-copy! v0 (gvector-n gv) (gvector-vec gv*) (gvector-n gv*)))
176+
130177

131178
;; gvector-set! with index = |gv| is interpreted as gvector-add!
132179
(define (gvector-set! gv index item)
133-
(let ([n (gvector-n gv)])
180+
(check-gvector 'gvector-set! gv)
181+
(let ([v (gvector-vec gv)]
182+
[n (gvector-n gv)])
134183
(check-index 'gvector-set! gv index #t)
135-
(if (= index n)
136-
(gvector-add! gv item)
137-
(vector-set! (gvector-vec gv) index item))))
184+
(if (unsafe-fx= index n)
185+
(if (impersonator? gv)
186+
(gvector-add! gv item)
187+
(unsafe-gvector-add! gv item))
188+
(unsafe-vector*-set! v index item))))
138189

139190
;; creates a snapshot vector
140191
(define (gvector->vector gv)
141-
(vector-copy (gvector-vec gv) 0 (gvector-n gv)))
192+
(check-gvector 'gvector->vector gv)
193+
(vector*-copy (gvector-vec gv) 0 (gvector-n gv)))
142194

143195
(define (gvector->list gv)
196+
(check-gvector 'gvector->list gv)
144197
(vector->list (gvector->vector gv)))
145198

146199
;; constructs a gvector
147200
(define (vector->gvector v)
201+
(unless (vector? v)
202+
(raise-argument-error* vector->gvector 'data/gvector "vector?" v))
148203
(define lv (vector-length v))
149-
(define gv (make-gvector #:capacity lv))
204+
(define gv (make-gvector #:capacity (max lv DEFAULT-CAPACITY)))
150205
(define nv (gvector-vec gv))
151206
(vector-copy! nv 0 v)
152207
(set-gvector-n! gv lv)
153208
gv)
154209

155210
(define (list->gvector v)
211+
(unless (list? v)
212+
(raise-argument-error* list->gvector 'data/gvector "list?" v))
156213
(vector->gvector (list->vector v)))
157214

158215
;; Iteration methods
@@ -165,8 +222,8 @@
165222
(define (gvector-iterate-next gv iter)
166223
(check-index 'gvector-iterate-next gv iter #f)
167224
(let ([n (gvector-n gv)])
168-
(and (< (add1 iter) n)
169-
(add1 iter))))
225+
(and (< (unsafe-fx+ 1 iter) n)
226+
(unsafe-fx+ 1 iter))))
170227

171228
(define (gvector-iterate-key gv iter)
172229
(check-index 'gvector-iterate-key gv iter #f)
@@ -177,8 +234,7 @@
177234
(gvector-ref gv iter))
178235

179236
(define (in-gvector gv)
180-
(unless (gvector? gv)
181-
(raise-type-error 'in-gvector "gvector" gv))
237+
(check-gvector 'in-gvector gv)
182238
(in-dict-values gv))
183239

184240
(define-sequence-syntax in-gvector*
@@ -192,11 +248,11 @@
192248
(:do-in ([(gv) gv-expr-c])
193249
(void) ;; outer-check; handled by contract
194250
([index 0] [vec (gvector-vec gv)] [n (gvector-n gv)]) ;; loop bindings
195-
(< index n) ;; pos-guard
196-
([(var) (vector-ref vec index)]) ;; inner bindings
251+
(unsafe-fx< index n) ;; pos-guard
252+
([(var) (unsafe-vector*-ref vec index)]) ;; inner bindings
197253
#t ;; pre-guard
198254
#t ;; post-guard
199-
((add1 index) (gvector-vec gv) (gvector-n gv)))]))]
255+
((unsafe-fx+ 1 index) vec n))]))]
200256
[[(var ...) (in-gv gv-expr)]
201257
(with-syntax ([gv-expr-c (wrap-expr/c #'gvector? #'gv-expr #:macro #'in-gv)])
202258
(syntax/loc stx
@@ -206,25 +262,34 @@
206262
(define-syntax (for/gvector stx)
207263
(syntax-case stx ()
208264
[(_ (clause ...) . body)
265+
#'(for/gvector #:capacity DEFAULT-CAPACITY (clause ...) . body)]
266+
[(_ #:capacity cap (clause ...) . body)
209267
(with-syntax ([((pre-body ...) post-body) (split-for-body stx #'body)])
210268
(quasisyntax/loc stx
211-
(let ([gv (make-gvector)])
269+
(let ([gv (make-gvector #:capacity cap)])
212270
(for/fold/derived #,stx () (clause ...)
213271
pre-body ...
214-
(call-with-values (lambda () . post-body)
215-
(lambda args (apply gvector-add! gv args) (values))))
272+
(call-with-values (lambda () . post-body)
273+
(case-lambda
274+
[(one) (unsafe-gvector-add! gv one)]
275+
[args (apply gvector-add! gv args)]))
276+
(values))
216277
gv)))]))
217278

218279
(define-syntax (for*/gvector stx)
219280
(syntax-case stx ()
220281
[(_ (clause ...) . body)
282+
#'(for/gvector #:capacity DEFAULT-CAPACITY (clause ...) . body)]
283+
[(_ #:capacity cap (clause ...) . body)
221284
(with-syntax ([((pre-body ...) post-body) (split-for-body stx #'body)])
222285
(quasisyntax/loc stx
223-
(let ([gv (make-gvector)])
286+
(let ([gv (make-gvector #:capacity cap)])
224287
(for*/fold/derived #,stx () (clause ...)
225288
pre-body ...
226289
(call-with-values (lambda () . post-body)
227-
(lambda args (apply gvector-add! gv args) (values))))
290+
(case-lambda
291+
[(one) (begin (unsafe-gvector-add! gv one) (values))]
292+
[args (begin (apply gvector-add! gv args) (values))])))
228293
gv)))]))
229294

230295
(struct gvector (vec n)
@@ -276,39 +341,26 @@
276341
#t
277342
(or (current-load-relative-directory) (current-directory))))
278343

279-
(provide/contract
280-
[gvector?
281-
(-> any/c any)]
282-
[rename gvector* gvector
283-
(->* () () #:rest any/c gvector?)]
284-
[make-gvector
285-
(->* () (#:capacity exact-positive-integer?) gvector?)]
286-
[gvector-ref
287-
(->* (gvector? exact-nonnegative-integer?) (any/c) any)]
288-
[gvector-set!
289-
(-> gvector? exact-nonnegative-integer? any/c any)]
290-
[gvector-add!
291-
(->* (gvector?) () #:rest any/c any)]
292-
[gvector-insert!
293-
(-> gvector? exact-nonnegative-integer? any/c any)]
294-
[gvector-remove!
295-
(-> gvector? exact-nonnegative-integer? any)]
296-
[gvector-remove-last!
297-
(-> gvector? any)]
298-
[gvector-count
299-
(-> gvector? any)]
300-
[gvector->vector
301-
(-> gvector? vector?)]
302-
[gvector->list
303-
(-> gvector? list?)]
304-
[vector->gvector
305-
(-> vector? gvector?)]
306-
[list->gvector
307-
(-> list? gvector?)])
308-
309-
(provide (rename-out [in-gvector* in-gvector])
310-
for/gvector
311-
for*/gvector)
344+
(provide
345+
gvector?
346+
(rename-out [gvector* gvector])
347+
make-gvector
348+
gvector-ref
349+
gvector-set!
350+
gvector-add!
351+
gvector-insert!
352+
gvector-remove!
353+
gvector-remove-last!
354+
gvector-append
355+
gvector-append!
356+
gvector-count
357+
gvector->vector
358+
gvector->list
359+
vector->gvector
360+
list->gvector
361+
(rename-out [in-gvector* in-gvector])
362+
for/gvector
363+
for*/gvector)
312364

313365
(module+ deserialize
314366
(provide deserialize-gvector)

data-lib/info.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#lang info
22
(define collection 'multi)
3-
(define deps '(("base" #:version "6.2.900.6")))
3+
(define deps '(("base" #:version "8.12.0.10")))
44
(define build-deps '("rackunit-lib"))
55

66
(define pkg-desc "implementation (no documentation) part of \"data\"")

0 commit comments

Comments
 (0)