Conversation
|
#18 is linked to this PR, as if it fixes the issue. But if I understand correctly, the issue is not fixed, right? |
Ah, I thought I had fixed that but now I did for real. |
* 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.
|
Performance compared to mut-treelist before: After: |
|
This is ready to merge, right? |
|
From my perspective yes although I was hoping for a review from @rmculpepper |
rmculpepper
left a comment
There was a problem hiding this comment.
Gvectors were previously unsynchronized and so not thread-safe. The introduction of unsafe operations makes them potentially memory-unsafe, and we need to make sure gvector operations, even used concurrently, are still memory-safe.
The gv-ensure-space! pattern helps: it means that writes to the vector have enough space, whether or not that vector is actually installed as the gvector's vec field at the end of the operation (eg, because of two racing add operations). I'm worried about the write to n afterwards; in a race, it might point past the end of vec.
I've pointed out a problem in gvector-ref if there is a concurrent call that shrinks the gvector. It also occurs if n is greater than the length of the vector. Other read operations (iteration, for example) might have similar problems, but I haven't checked them all.
If unsafe operations are used, we need to figure out and document invariants and patterns of field updates that actually preserve memory-safety.
| (define v (ensure-free-space-vec! (gvector-vec gv) (gvector-n gv) needed-free-space)) | ||
| (when v (set-gvector-vec! gv v))) | ||
|
|
||
| (define-syntax-rule (gv-ensure-space! gv n v needed-free-space) |
There was a problem hiding this comment.
I think a syntax like (define/ensure-space! (n v) gv needed-free-space) would better signal what this macro is doing.
| (raise-type-error 'gvector-ref "exact nonnegative integer" index)) | ||
| (if (< index (gvector-n gv)) | ||
| (vector-ref (gvector-vec gv) index) | ||
| (unsafe-vector*-ref (gvector-vec gv) index) |
There was a problem hiding this comment.
I think this is unsafe if a concurrent call to remove shrinks the vector. That is, if (gvector-n gv) in the previous line is fetched before the call to remove and (gvector-vec gv) is fetched afterwards, the n might be stale and the vector can be too short.
|
|
||
| ;; gvector-set! with index = |gv| is interpreted as gvector-add! | ||
| (define (gvector-set! gv index item) | ||
| (check-gvector 'gvector-set gv) |
There was a problem hiding this comment.
should be 'gvector-set!
| (gvector-add! gv item) | ||
| (vector-set! (gvector-vec gv) index item)))) | ||
| (if (unsafe-fx= index n) | ||
| (unsafe-gvector-add! gv item) |
There was a problem hiding this comment.
I don't see why unsafe-gvector-add!'s precondition (not a chaperone) is satisfied here.
single-value performance (see Optimizations and benchmarks for gvectors racket#4942).
(taken from Rust's Vector implementation).
vector-extendfrom Addvector-extend. racket#4943.