Skip to content

Commit 569472f

Browse files
committed
Revise for better performance.
* Eliminates contracts in favor of inline checks. * Specializes multiple-value code to improve single-value performance. * Use simpler non-recursive growth computation (taken from Rust's Vector implementation). * Avoid duplicate work in core operations. * Add #:capacity arguments. * Use unsafe operations including `unsafe-make-vector` from racket/racket#4943.
1 parent 0f85d3c commit 569472f

1 file changed

Lines changed: 107 additions & 78 deletions

File tree

data-lib/data/gvector.rkt

Lines changed: 107 additions & 78 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-positive-integer? capacity)
21+
(raise-argument-error* 'make-gvector 'data/gvector "exact-positive-integer?" capacity))
22+
(gvector (make-vector (max capacity MIN-CAPACITY) 0) 0))
1623

1724
(define gvector*
1825
(let ([gvector
@@ -29,43 +36,61 @@
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+
(define new-vec
56+
(vector-append
57+
vec
58+
;; An optimization could eliminate this subtraction
59+
(unsafe-make-vector (unsafe-fx- new-cap cap) 0)))
60+
new-vec)]))
61+
62+
(define (ensure-free-space! gv needed-free-space)
63+
(define v (ensure-free-space-vec! (gvector-vec gv) (gvector-n gv) needed-free-space))
64+
(when v (set-gvector-vec! gv v)))
65+
66+
(define-syntax-rule (gv-ensure-space! gv n v needed-free-space)
67+
(begin (define n (gvector-n gv))
68+
(define v1 (gvector-vec gv))
69+
(define v2 (ensure-free-space-vec! v1 n needed-free-space))
70+
(define v (if v2 (begin (set-gvector-vec! gv v2) v2) v1))))
71+
72+
(define gvector-add!
73+
(case-lambda
74+
[(gv item)
75+
(check-gvector 'gvector-add! gv)
76+
(gv-ensure-space! gv n v 1)
77+
(unsafe-vector*-set! v n item)
78+
(set-gvector-n! gv (unsafe-fx+ 1 n))]
79+
[(gv . items)
80+
(check-gvector 'gvector-add! gv)
81+
(define item-count (length items))
82+
(gv-ensure-space! gv n v item-count)
83+
(for ([index (in-naturals n)] [item (in-list items)])
84+
(unsafe-vector*-set! v index item))
85+
(set-gvector-n! gv (+ n item-count))])))
6286

6387
;; SLOW!
6488
(define (gvector-insert! gv index item)
6589
;; This does (n - index) redundant copies on resize, but that
6690
;; happens rarely and I prefer the simpler code.
67-
(define n (gvector-n gv))
91+
(check-gvector 'gvector-insert! gv)
6892
(check-index 'gvector-insert! gv index #t)
93+
(define n (gvector-n gv))
6994
(ensure-free-space! gv 1)
7095
(define v (gvector-vec gv))
7196
(vector-copy! v (add1 index) v index n)
@@ -97,6 +122,7 @@
97122

98123
;; SLOW!
99124
(define (gvector-remove! gv index)
125+
(check-gvector 'gvector-remove! gv)
100126
(define n (gvector-n gv))
101127
(define v (gvector-vec gv))
102128
(check-index 'gvector-remove! gv index #f)
@@ -106,6 +132,7 @@
106132
(trim! gv))
107133

108134
(define (gvector-remove-last! gv)
135+
(check-gvector 'gvector-remove-last! gv)
109136
(let ([n (gvector-n gv)]
110137
[v (gvector-vec gv)])
111138
(unless (> n 0) (error 'gvector-remove-last! "empty"))
@@ -114,45 +141,54 @@
114141
last-val))
115142

116143
(define (gvector-count gv)
144+
(check-gvector 'gvector-count gv)
117145
(gvector-n gv))
118146

119147
(define none (gensym 'none))
120148

121149
(define (gvector-ref gv index [default none])
150+
(check-gvector 'gvector-ref gv)
122151
(unless (exact-nonnegative-integer? index)
123152
(raise-type-error 'gvector-ref "exact nonnegative integer" index))
124153
(if (< index (gvector-n gv))
125-
(vector-ref (gvector-vec gv) index)
154+
(unsafe-vector*-ref (gvector-vec gv) index)
126155
(cond [(eq? default none)
127156
(check-index 'gvector-ref gv index #f)]
128157
[(procedure? default) (default)]
129158
[else default])))
130159

131160
;; gvector-set! with index = |gv| is interpreted as gvector-add!
132161
(define (gvector-set! gv index item)
162+
(check-gvector 'gvector-set gv)
133163
(let ([n (gvector-n gv)])
134164
(check-index 'gvector-set! gv index #t)
135165
(if (= index n)
136166
(gvector-add! gv item)
137-
(vector-set! (gvector-vec gv) index item))))
167+
(unsafe-vector*-set! (gvector-vec gv) index item))))
138168

139169
;; creates a snapshot vector
140170
(define (gvector->vector gv)
171+
(check-gvector 'gvector->vector gv)
141172
(vector-copy (gvector-vec gv) 0 (gvector-n gv)))
142173

143174
(define (gvector->list gv)
175+
(check-gvector 'gvector->list gv)
144176
(vector->list (gvector->vector gv)))
145177

146178
;; constructs a gvector
147179
(define (vector->gvector v)
180+
(unless (vector? v)
181+
(raise-argument-error* vector->gvector 'data/gvector "vector?" v))
148182
(define lv (vector-length v))
149-
(define gv (make-gvector #:capacity lv))
183+
(define gv (make-gvector #:capacity (max lv DEFAULT-CAPACITY)))
150184
(define nv (gvector-vec gv))
151185
(vector-copy! nv 0 v)
152186
(set-gvector-n! gv lv)
153187
gv)
154188

155189
(define (list->gvector v)
190+
(unless (list? v)
191+
(raise-argument-error* list->gvector 'data/gvector "list?" v))
156192
(vector->gvector (list->vector v)))
157193

158194
;; Iteration methods
@@ -177,8 +213,7 @@
177213
(gvector-ref gv iter))
178214

179215
(define (in-gvector gv)
180-
(unless (gvector? gv)
181-
(raise-type-error 'in-gvector "gvector" gv))
216+
(check-gvector 'in-gvector gv)
182217
(in-dict-values gv))
183218

184219
(define-sequence-syntax in-gvector*
@@ -192,11 +227,11 @@
192227
(:do-in ([(gv) gv-expr-c])
193228
(void) ;; outer-check; handled by contract
194229
([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
230+
(unsafe-fx< index n) ;; pos-guard
231+
([(var) (unsafe-vector*-ref vec index)]) ;; inner bindings
197232
#t ;; pre-guard
198233
#t ;; post-guard
199-
((add1 index) (gvector-vec gv) (gvector-n gv)))]))]
234+
((unsafe-fx+ 1 index) vec n))]))]
200235
[[(var ...) (in-gv gv-expr)]
201236
(with-syntax ([gv-expr-c (wrap-expr/c #'gvector? #'gv-expr #:macro #'in-gv)])
202237
(syntax/loc stx
@@ -206,25 +241,34 @@
206241
(define-syntax (for/gvector stx)
207242
(syntax-case stx ()
208243
[(_ (clause ...) . body)
244+
#'(for/gvector #:capacity DEFAULT-CAPACITY (clause ...) . body)]
245+
[(_ #:capacity cap (clause ...) . body)
209246
(with-syntax ([((pre-body ...) post-body) (split-for-body stx #'body)])
210247
(quasisyntax/loc stx
211-
(let ([gv (make-gvector)])
248+
(let ([gv (make-gvector #:capacity cap)])
212249
(for/fold/derived #,stx () (clause ...)
213250
pre-body ...
214-
(call-with-values (lambda () . post-body)
215-
(lambda args (apply gvector-add! gv args) (values))))
251+
(call-with-values (lambda () . post-body)
252+
(case-lambda
253+
[(one) (gvector-add! gv one)]
254+
[args (apply gvector-add! gv args)]))
255+
(values))
216256
gv)))]))
217257

218258
(define-syntax (for*/gvector stx)
219259
(syntax-case stx ()
220260
[(_ (clause ...) . body)
261+
#'(for/gvector #:capacity DEFAULT-CAPACITY (clause ...) . body)]
262+
[(_ #:capacity cap (clause ...) . body)
221263
(with-syntax ([((pre-body ...) post-body) (split-for-body stx #'body)])
222264
(quasisyntax/loc stx
223-
(let ([gv (make-gvector)])
265+
(let ([gv (make-gvector #:capacity cap)])
224266
(for*/fold/derived #,stx () (clause ...)
225267
pre-body ...
226268
(call-with-values (lambda () . post-body)
227-
(lambda args (apply gvector-add! gv args) (values))))
269+
(case-lambda
270+
[(one) (begin (gvector-add! gv one) (values))]
271+
[args (begin (apply gvector-add! gv args) (values))])))
228272
gv)))]))
229273

230274
(struct gvector (vec n)
@@ -276,39 +320,24 @@
276320
#t
277321
(or (current-load-relative-directory) (current-directory))))
278322

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)
323+
(provide
324+
gvector?
325+
(rename-out [gvector* gvector])
326+
make-gvector
327+
gvector-ref
328+
gvector-set!
329+
gvector-add!
330+
gvector-insert!
331+
gvector-remove!
332+
gvector-remove-last!
333+
gvector-count
334+
gvector->vector
335+
gvector->list
336+
vector->gvector
337+
list->gvector
338+
(rename-out [in-gvector* in-gvector])
339+
for/gvector
340+
for*/gvector)
312341

313342
(module+ deserialize
314343
(provide deserialize-gvector)

0 commit comments

Comments
 (0)