Skip to content

Commit 318152b

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

File tree

2 files changed

+117
-84
lines changed

2 files changed

+117
-84
lines changed

data-lib/data/gvector.rkt

Lines changed: 116 additions & 83 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,65 @@
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+
;; An optimization could eliminate this subtraction
57+
(vector*-extend vec (unsafe-fx- new-cap cap) 0))
58+
new-vec)]))
59+
60+
(define (ensure-free-space! gv needed-free-space)
61+
(define v (ensure-free-space-vec! (gvector-vec gv) (gvector-n gv) needed-free-space))
62+
(when v (set-gvector-vec! gv v)))
63+
64+
(define-syntax-rule (gv-ensure-space! gv n v needed-free-space)
65+
(begin (define n (gvector-n gv))
66+
(define v1 (gvector-vec gv))
67+
(define v2 (ensure-free-space-vec! v1 n needed-free-space))
68+
(define v (if v2 (begin (set-gvector-vec! gv v2) v2) v1))))
69+
70+
;; only safe on unchaperoned gvectors
71+
(define (unsafe-gvector-add! gv item)
72+
(gv-ensure-space! gv n v 1)
73+
(unsafe-vector*-set! v n item)
74+
(set-gvector-n! gv (unsafe-fx+ 1 n)))
75+
76+
(define gvector-add!
77+
(case-lambda
78+
[(gv item)
79+
(check-gvector 'gvector-add! gv)
80+
(gv-ensure-space! gv n v 1)
81+
(unsafe-vector*-set! v n item)
82+
(set-gvector-n! gv (unsafe-fx+ 1 n))]
83+
[(gv . items)
84+
(check-gvector 'gvector-add! gv)
85+
(define item-count (length items))
86+
(gv-ensure-space! gv n v item-count)
87+
(for ([index (in-naturals n)] [item (in-list items)])
88+
(unsafe-vector*-set! v index item))
89+
(set-gvector-n! gv (+ n item-count))])))
6290

6391
;; SLOW!
6492
(define (gvector-insert! gv index item)
6593
;; This does (n - index) redundant copies on resize, but that
6694
;; happens rarely and I prefer the simpler code.
67-
(define n (gvector-n gv))
95+
(check-gvector 'gvector-insert! gv)
6896
(check-index 'gvector-insert! gv index #t)
97+
(define n (gvector-n gv))
6998
(ensure-free-space! gv 1)
7099
(define v (gvector-vec gv))
71100
(vector-copy! v (add1 index) v index n)
@@ -97,6 +126,7 @@
97126

98127
;; SLOW!
99128
(define (gvector-remove! gv index)
129+
(check-gvector 'gvector-remove! gv)
100130
(define n (gvector-n gv))
101131
(define v (gvector-vec gv))
102132
(check-index 'gvector-remove! gv index #f)
@@ -106,6 +136,7 @@
106136
(trim! gv))
107137

108138
(define (gvector-remove-last! gv)
139+
(check-gvector 'gvector-remove-last! gv)
109140
(let ([n (gvector-n gv)]
110141
[v (gvector-vec gv)])
111142
(unless (> n 0) (error 'gvector-remove-last! "empty"))
@@ -114,45 +145,54 @@
114145
last-val))
115146

116147
(define (gvector-count gv)
148+
(check-gvector 'gvector-count gv)
117149
(gvector-n gv))
118150

119151
(define none (gensym 'none))
120152

121153
(define (gvector-ref gv index [default none])
154+
(check-gvector 'gvector-ref gv)
122155
(unless (exact-nonnegative-integer? index)
123156
(raise-type-error 'gvector-ref "exact nonnegative integer" index))
124157
(if (< index (gvector-n gv))
125-
(vector-ref (gvector-vec gv) index)
158+
(unsafe-vector*-ref (gvector-vec gv) index)
126159
(cond [(eq? default none)
127160
(check-index 'gvector-ref gv index #f)]
128161
[(procedure? default) (default)]
129162
[else default])))
130163

131164
;; gvector-set! with index = |gv| is interpreted as gvector-add!
132165
(define (gvector-set! gv index item)
166+
(check-gvector 'gvector-set gv)
133167
(let ([n (gvector-n gv)])
134168
(check-index 'gvector-set! gv index #t)
135-
(if (= index n)
136-
(gvector-add! gv item)
137-
(vector-set! (gvector-vec gv) index item))))
169+
(if (unsafe-fx= index n)
170+
(unsafe-gvector-add! gv item)
171+
(unsafe-vector*-set! (gvector-vec gv) index item))))
138172

139173
;; creates a snapshot vector
140174
(define (gvector->vector gv)
141-
(vector-copy (gvector-vec gv) 0 (gvector-n gv)))
175+
(check-gvector 'gvector->vector gv)
176+
(vector*-copy (gvector-vec gv) 0 (gvector-n gv)))
142177

143178
(define (gvector->list gv)
179+
(check-gvector 'gvector->list gv)
144180
(vector->list (gvector->vector gv)))
145181

146182
;; constructs a gvector
147183
(define (vector->gvector v)
184+
(unless (vector? v)
185+
(raise-argument-error* vector->gvector 'data/gvector "vector?" v))
148186
(define lv (vector-length v))
149-
(define gv (make-gvector #:capacity lv))
187+
(define gv (make-gvector #:capacity (max lv DEFAULT-CAPACITY)))
150188
(define nv (gvector-vec gv))
151189
(vector-copy! nv 0 v)
152190
(set-gvector-n! gv lv)
153191
gv)
154192

155193
(define (list->gvector v)
194+
(unless (list? v)
195+
(raise-argument-error* list->gvector 'data/gvector "list?" v))
156196
(vector->gvector (list->vector v)))
157197

158198
;; Iteration methods
@@ -165,8 +205,8 @@
165205
(define (gvector-iterate-next gv iter)
166206
(check-index 'gvector-iterate-next gv iter #f)
167207
(let ([n (gvector-n gv)])
168-
(and (< (add1 iter) n)
169-
(add1 iter))))
208+
(and (< (unsafe-fx+ 1 iter) n)
209+
(unsafe-fx+ 1 iter))))
170210

171211
(define (gvector-iterate-key gv iter)
172212
(check-index 'gvector-iterate-key gv iter #f)
@@ -177,8 +217,7 @@
177217
(gvector-ref gv iter))
178218

179219
(define (in-gvector gv)
180-
(unless (gvector? gv)
181-
(raise-type-error 'in-gvector "gvector" gv))
220+
(check-gvector 'in-gvector gv)
182221
(in-dict-values gv))
183222

184223
(define-sequence-syntax in-gvector*
@@ -192,11 +231,11 @@
192231
(:do-in ([(gv) gv-expr-c])
193232
(void) ;; outer-check; handled by contract
194233
([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
234+
(unsafe-fx< index n) ;; pos-guard
235+
([(var) (unsafe-vector*-ref vec index)]) ;; inner bindings
197236
#t ;; pre-guard
198237
#t ;; post-guard
199-
((add1 index) (gvector-vec gv) (gvector-n gv)))]))]
238+
((unsafe-fx+ 1 index) vec n))]))]
200239
[[(var ...) (in-gv gv-expr)]
201240
(with-syntax ([gv-expr-c (wrap-expr/c #'gvector? #'gv-expr #:macro #'in-gv)])
202241
(syntax/loc stx
@@ -206,25 +245,34 @@
206245
(define-syntax (for/gvector stx)
207246
(syntax-case stx ()
208247
[(_ (clause ...) . body)
248+
#'(for/gvector #:capacity DEFAULT-CAPACITY (clause ...) . body)]
249+
[(_ #:capacity cap (clause ...) . body)
209250
(with-syntax ([((pre-body ...) post-body) (split-for-body stx #'body)])
210251
(quasisyntax/loc stx
211-
(let ([gv (make-gvector)])
252+
(let ([gv (make-gvector #:capacity cap)])
212253
(for/fold/derived #,stx () (clause ...)
213254
pre-body ...
214-
(call-with-values (lambda () . post-body)
215-
(lambda args (apply gvector-add! gv args) (values))))
255+
(call-with-values (lambda () . post-body)
256+
(case-lambda
257+
[(one) (unsafe-gvector-add! gv one)]
258+
[args (apply gvector-add! gv args)]))
259+
(values))
216260
gv)))]))
217261

218262
(define-syntax (for*/gvector stx)
219263
(syntax-case stx ()
220264
[(_ (clause ...) . body)
265+
#'(for/gvector #:capacity DEFAULT-CAPACITY (clause ...) . body)]
266+
[(_ #:capacity cap (clause ...) . body)
221267
(with-syntax ([((pre-body ...) post-body) (split-for-body stx #'body)])
222268
(quasisyntax/loc stx
223-
(let ([gv (make-gvector)])
269+
(let ([gv (make-gvector #:capacity cap)])
224270
(for*/fold/derived #,stx () (clause ...)
225271
pre-body ...
226272
(call-with-values (lambda () . post-body)
227-
(lambda args (apply gvector-add! gv args) (values))))
273+
(case-lambda
274+
[(one) (begin (unsafe-gvector-add! gv one) (values))]
275+
[args (begin (apply gvector-add! gv args) (values))])))
228276
gv)))]))
229277

230278
(struct gvector (vec n)
@@ -276,39 +324,24 @@
276324
#t
277325
(or (current-load-relative-directory) (current-directory))))
278326

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

313346
(module+ deserialize
314347
(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)