updated splay-tree & skip-list interfaces, docs, tests, performance tests

This commit is contained in:
Ryan Culpepper 2010-09-16 12:05:51 -06:00
parent 12e2d6d76a
commit 1a03a47842
5 changed files with 1051 additions and 455 deletions

View File

@ -25,10 +25,13 @@ A skip-list is a dictionary (@racket[dict?] from
dictionary interface for iterator-based search and mutation.
@defproc[(make-skip-list [=? (any/c any/c . -> . any/c)]
[<? (any/c any/c . -> . any/c)])
[<? (any/c any/c . -> . any/c)]
[#:key-contract key-contract contract? any/c]
[#:value-contract value-contract contract? any/c])
skip-list?]{
Makes a new empty skip-list. The skip-list uses @racket[=?] and @racket[<?] to order keys.
Makes a new empty skip-list. The skip-list uses @racket[=?] and
@racket[<?] to order keys.
@examples[#:eval the-eval
(define skip-list (make-skip-list = <))

View File

@ -44,19 +44,18 @@ Makes a new empty splay-tree. The splay tree uses @racket[=?] and
]
}
@defproc[(make-integer-splay-tree [#:adjust? adjust boolean? #f]
[#:key-contract key-contract contract? any/c]
[#:value-contract value-contract contract? any/c])
@defproc[(make-adjustable-splay-tree
[#:key-contract key-contract contract? any/c]
[#:value-contract value-contract contract? any/c])
splay-tree?]{
Makes a new empty splay-tree that permits only exact integers as keys
(in addition to any constraints imposed by @racket[key-contract]). If
@racket[adjust?] is true, then the resulting splay tree answers true
to @racket[splay-tree-with-adjust?] and supports efficient key
adjustment.
(in addition to any constraints imposed by @racket[key-contract]). The
resulting splay tree answers true to @racket[adjustable-splay-tree?]
and supports efficient key adjustment.
@examples[#:eval the-eval
(define splay-tree (make-integer-splay-tree))
(define splay-tree (make-adjustable-splay-tree))
(splay-tree-set! splay-tree 3 'apple)
(splay-tree-set! splay-tree 6 'cherry)
(dict-map splay-tree list)
@ -71,7 +70,7 @@ adjustment.
Returns @racket[#t] if @racket[x] is a splay-tree, @racket[#f] otherwise.
}
@defproc[(splay-tree-with-adjust? [s splay-tree?]) boolean?]{
@defproc[(adjustable-splay-tree? [x any/c]) boolean?]{
Returns @racket[#t] if @racket[x] is a splay-tree that supports key
adjustment; see @racket[splay-tree-contract!] and
@ -114,7 +113,7 @@ Removes all keys in [@racket[from], @racket[to]); that is, all keys
greater than or equal to @racket[from] and less than @racket[to].
}
@defproc[(splay-tree-contract! [s (and/c splay-tree? splay-tree-with-adjust?)]
@defproc[(splay-tree-contract! [s adjustable-splay-tree?]
[from any/c] [to any/c])
void?]{
@ -123,7 +122,7 @@ of all keys greater than or equal to @racket[to] by @racket[(- to
from)].
}
@defproc[(splay-tree-expand! [s (and/c splay-tree? splay-tree-with-adjust?)]
@defproc[(splay-tree-expand! [s adjustable-splay-tree?]
[from any/c] [to any/c])
void?]{

View File

@ -144,9 +144,6 @@ Levels are indexed starting at 1, as in the paper.
;; Skip list
(define (make-skip-list =? <?)
(skip-list (vector 'head 'head #f) 0 =? <?))
(define (skip-list-ref s key [default (skip-list-error key)])
(define head (skip-list-head s))
(define result
@ -281,39 +278,73 @@ Levels are indexed starting at 1, as in the paper.
(check-iter 'skip-list-iterate-set-value! s iter)
(set-item-data! (skip-list-iter-item iter) value))
(define dict-methods
(vector-immutable skip-list-ref
skip-list-set!
#f ;; set
skip-list-remove!
#f ;; remove
skip-list-count
skip-list-iterate-first
skip-list-iterate-next
skip-list-iterate-key
skip-list-iterate-value))
(define ordered-dict-methods
(vector-immutable skip-list-iterate-min
skip-list-iterate-max
skip-list-iterate-least/>?
skip-list-iterate-least/>=?
skip-list-iterate-greatest/<?
skip-list-iterate-greatest/<=?))
(struct skip-list ([head #:mutable] [num-entries #:mutable] =? <?)
#:property prop:dict/contract
(list (vector-immutable skip-list-ref
skip-list-set!
#f ;; set
skip-list-remove!
#f ;; remove
skip-list-count
skip-list-iterate-first
skip-list-iterate-next
skip-list-iterate-key
skip-list-iterate-value)
(vector-immutable any/c any/c skip-list-iter?
#f #f #f))
#:property prop:ordered-dict
(vector-immutable skip-list-iterate-min
skip-list-iterate-max
skip-list-iterate-least/>?
skip-list-iterate-least/>=?
skip-list-iterate-greatest/<?
skip-list-iterate-greatest/<=?))
#:property prop:dict/contract
(list dict-methods
(vector-immutable any/c any/c skip-list-iter?
#f #f #f))
#:property prop:ordered-dict ordered-dict-methods)
(struct skip-list* skip-list (key-c value-c)
#:property prop:dict/contract
(list dict-methods
(vector-immutable any/c any/c skip-list-iter?
(lambda (s) (skip-list*-key-c s))
(lambda (s) (skip-list*-value-c s))
#f))
#:property prop:ordered-dict ordered-dict-methods)
(define (make-skip-list =? <?
#:key-contract [key-contract any/c]
#:value-contract [value-contract any/c])
(cond [(and (eq? key-contract any/c) (eq? value-contract any/c))
(skip-list (vector 'head 'head #f) 0 =? <?)]
[else
(skip-list* (vector 'head 'head #f) 0 =? <? key-contract value-contract)]))
(define (key-c s)
(cond [(skip-list*? s) (skip-list*-key-c s)]
[else any/c]))
(define (val-c s)
(cond [(skip-list*? s) (skip-list*-value-c s)]
[else any/c]))
(provide/contract
[make-skip-list
(-> procedure? procedure? skip-list?)]
(->* ((-> any/c any/c any/c) (-> any/c any/c any/c))
(#:key-contract contract? #:value-contract contract?)
skip-list?)]
[skip-list?
(-> any/c boolean?)]
[skip-list-ref
(->* (skip-list? any/c) (any/c) any)]
(->i ([s skip-list?] [k (s) (key-c s)])
([d any/c])
any)]
[skip-list-set!
(-> skip-list? any/c any/c void?)]
(->i ([s skip-list?] [k (s) (key-c s)] [v (s) (val-c s)]) [_ void?])]
[skip-list-remove!
(-> skip-list? any/c void?)]
(->i ([s skip-list?] [k (s) (key-c s)]) [_ void?])]
[skip-list-count
(-> skip-list? exact-nonnegative-integer?)]
[skip-list-iterate-first
@ -321,18 +352,18 @@ Levels are indexed starting at 1, as in the paper.
[skip-list-iterate-next
(-> skip-list? skip-list-iter? (or/c skip-list-iter? #f))]
[skip-list-iterate-key
(-> skip-list? skip-list-iter? any)]
(->i ([s skip-list?] [i skip-list-iter?]) [_ (s) (key-c s)])]
[skip-list-iterate-value
(-> skip-list? skip-list-iter? any)]
(->i ([s skip-list?] [i skip-list-iter?]) [_ (s) (val-c s)])]
[skip-list-iterate-greatest/<?
(-> skip-list? any/c (or/c skip-list-iter? #f))]
[skip-list-iterate-greatest/<=?
(-> skip-list? any/c (or/c skip-list-iter? #f))]
[skip-list-iterate-least/>?
(-> skip-list? any/c (or/c skip-list-iter? #f))]
(->i ([s skip-list?] [k (s) (key-c s)]) [_ (or/c skip-list-iter? #f)])]
[skip-list-iterate-greatest/<?
(->i ([s skip-list?] [k (s) (key-c s)]) [_ (or/c skip-list-iter? #f)])]
[skip-list-iterate-least/>=?
(-> skip-list? any/c (or/c skip-list-iter? #f))]
(->i ([s skip-list?] [k (s) (key-c s)]) [_ (or/c skip-list-iter? #f)])]
[skip-list-iterate-least/>?
(->i ([s skip-list?] [k (s) (key-c s)]) [_ (or/c skip-list-iter? #f)])]
[skip-list-iterate-min
(-> skip-list? (or/c skip-list-iter? #f))]
@ -340,45 +371,9 @@ Levels are indexed starting at 1, as in the paper.
(-> skip-list? (or/c skip-list-iter? #f))]
[skip-list-iterate-set-key!
(-> skip-list? skip-list-iter? any/c any)]
(->i ([s skip-list?] [i skip-list-iter?] [k (s) (key-c s)]) [_ void?])]
[skip-list-iterate-set-value!
(-> skip-list? skip-list-iter? any/c any)]
(->i ([s skip-list?] [i skip-list-iter?] [v (s) (val-c s)]) [_ void?])]
[skip-list-iter?
(-> any/c any)])
#|
;; Testing
(define s (make-skip-list* = <))
s
(dict-map s list)
(skip-list-set! s 1 'apple)
(skip-list-set! s 3 'pear)
(skip-list-set! s 2 'orange)
(dict-map s list)
(define h
(time
(for/hash ([n (in-range 1 50000)])
(values (random 1000) n))))
(define s2 (make-skip-list* = <))
(time
(for ([n (in-range 1 50000)])
(skip-list-set! s2 (random 1000) n)))
(define d (make-skip-list* = <))
(time
(for ([n (in-range 1 50000)])
(dict-set! d (random 1000) n)))
(define (find-a-bunch t)
(for ([n (in-range 1 10000)])
(dict-ref t (random 1000) #f)))
(display "\nlookup 10000 times\n")
;(time (find-a-bunch h))
(time (find-a-bunch s2))
|#

File diff suppressed because it is too large Load Diff

View File

@ -7,36 +7,60 @@
;; Tests for ordered dictionaries
;; - skip-list
;; - splay-tree
;; - splay-tree (both kinds)
(define (rand-test dicts ordered?)
(define-syntax-rule (rand-test dicts ordered? idk?
(-ref
-set!
-remove!
-count
-has-key?
-iterate-key
-iterate-least/>?
-iterate-least/>=?
-iterate-greatest/<?
-iterate-greatest/<=?))
(let ([hash (make-hash)])
(for ([c (in-range 100)])
(let* ([k (- (random 2000) 1000)]
[v (- (random 2000) 1000)])
(for ([d (cons hash dicts)])
(dict-set! d k v))))
(hash-set! hash k v)
(for ([d dicts])
(-set! d k v))))
(for ([d dicts])
(check-equal? (hash-count hash)
(-count d)))
;; Sequential access
(for ([i (in-range -1000 1000)])
(for ([d dicts])
(let ([vh (hash-ref hash i 'not-there)]
[vd (dict-ref d i 'not-there)])
[vd (-ref d i 'not-there)])
(check-equal? vh vd (format "dict ~e, key = ~s, value = ~s, expected = ~s"
d i vd vh)))))
;; Random removal
(for ([i (in-range 500)])
(let ([k (- (random 2000) 1000)])
(for ([d dicts])
(hash-remove! hash k)
(-remove! d k)
(check-equal? (hash-count hash) (-count d)))))
(when ordered?
(for ([c (in-range 100)])
(let* ([k0 (- (random 2000) 1000)])
(for ([d dicts])
(let* ([has? (dict-has-key? d k0)]
[l>i (dict-iterate-least/>? d k0)]
[l>=i (dict-iterate-least/>=? d k0)]
[g<i (dict-iterate-greatest/<? d k0)]
[g<=i (dict-iterate-greatest/<=? d k0)]
[l> (and l>i (dict-iterate-key d l>i))]
[l>= (and l>=i (dict-iterate-key d l>=i))]
[g< (and g<i (dict-iterate-key d g<i))]
[g<= (and g<=i (dict-iterate-key d g<=i))])
(let* ([has? (-has-key? d k0)]
[l>i (-iterate-least/>? d k0)]
[l>=i (-iterate-least/>=? d k0)]
[g<i (-iterate-greatest/<? d k0)]
[g<=i (-iterate-greatest/<=? d k0)]
[l> (and l>i (-iterate-key d l>i))]
[l>= (and l>=i (-iterate-key d l>=i))]
[g< (and g<i (-iterate-key d g<i))]
[g<= (and g<=i (-iterate-key d g<=i))])
(when has?
(check-equal? l>= g<= "has, should be same"))
(unless has?
@ -46,55 +70,184 @@
(when l>= (check >= l>= k0))
(when g< (check < g< k0))
(when g<= (check <= g<= k0))
(for ([k (in-dict-keys d)])
(when (and l> (and (> k k0) (< k l>))) (error "l>"))
(when (and l>= (and (>= k k0) (< k l>=))) (error "l>="))
(when (and g< (and (< k k0) (> k g<))) (error "g<"))
(when (and g<= (and (<= k k0) (> k g<=))) (error "g<="))))))))))
(when idk?
(for ([k (in-dict-keys d)])
(when (and l> (and (> k k0) (< k l>))) (error "l>"))
(when (and l>= (and (>= k k0) (< k l>=))) (error "l>="))
(when (and g< (and (< k k0) (> k g<))) (error "g<"))
(when (and g<= (and (<= k k0) (> k g<=))) (error "g<=")))))))))))
(test-case "skip-list tests"
(rand-test (list (make-skip-list = <)) #t))
;; Test dict interface
(test-case "splay-tree test"
(rand-test (list (make-splay-tree = <)) #t))
(define (dict-test dicts ordered? [idk? #f])
(rand-test dicts ordered? idk?
(dict-ref
dict-set!
dict-remove!
dict-count
dict-has-key?
dict-iterate-key
dict-iterate-least/>?
dict-iterate-least/>=?
dict-iterate-greatest/<?
dict-iterate-greatest/<=?)))
(test-case "int-splay-tree w adjust"
(rand-test (list (make-integer-splay-tree #:adjust? #t)) #t))
(test-case "skip-list, dict interface"
(dict-test (list (make-skip-list = <)) #t #t))
(test-case "splay-tree, dict interface"
(dict-test (list (make-splay-tree = <)) #t #t))
(test-case "adjustable-splay-tree, dict interface"
(dict-test (list (make-adjustable-splay-tree)) #t #t))
(provide rand-test)
(provide dict-test)
#|
(define (splay-test splays _eh)
(let ([hash (make-hash)])
(for ([c (in-range 100)])
(let* ([k (- (random 2000) 1000)]
[v (- (random 2000) 1000)])
(hash-set! hash k v)
(for ([d splays])
(splay-tree-set! d k v))))
;; Test splay-tree interface
(for ([i (in-range -1000 1000)])
(for ([d splays])
(let ([vh (hash-ref hash i 'not-there)]
[vd (splay-tree-ref d i 'not-there)])
(check-equal? vh vd (format "dict ~e, key = ~s, value = ~s, expected = ~s"
d i vd vh)))))))
(define (splay-test dicts ordered? [idk? #f])
(rand-test dicts ordered? idk?
(splay-tree-ref
splay-tree-set!
splay-tree-remove!
splay-tree-count
dict-has-key?
splay-tree-iterate-key
splay-tree-iterate-least/>?
splay-tree-iterate-least/>=?
splay-tree-iterate-greatest/<?
splay-tree-iterate-greatest/<=?)))
(test-case "splay-tree, splay-tree interface"
(splay-test (list (make-splay-tree = <)) #t #t))
(test-case "adjustable-splay-tree, splay-tree interface"
(splay-test (list (make-adjustable-splay-tree)) #t #t))
(provide splay-test)
(require (prefix-in ud: racket/private/dict)
(prefix-in cd: racket/dict))
;; Test skip-list interface
(define-syntax-rule (htest *dict-set! *dict-ref)
(let ([h (make-hash)])
(for ([c (in-range 100)])
(*dict-set! h (- (random 2000) 1000) (- (random 2000) 1000)))
(for ([i (in-range -1000 1000)])
(*dict-ref h i 'not-there))))
(define (skip-test dicts ordered? [idk? #f])
(rand-test dicts ordered? idk?
(skip-list-ref
skip-list-set!
skip-list-remove!
skip-list-count
dict-has-key?
skip-list-iterate-key
skip-list-iterate-least/>?
skip-list-iterate-least/>=?
skip-list-iterate-greatest/<?
skip-list-iterate-greatest/<=?)))
(define (ud-test) (htest ud:dict-set! ud:dict-ref))
(define (cd-test) (htest cd:dict-set! cd:dict-ref))
(test-case "skip-list, skip-list interface"
(skip-test (list (make-skip-list = <)) #t #t))
(provide skip-test)
;; Test hash interface
;; for speed comparison only
(define (hash-test dicts ordered?)
(when ordered?
(error 'hash-tests "ordered not supported"))
(rand-test dicts #f #f
(hash-ref
hash-set!
hash-remove!
hash-count
hash-has-key?
'-iterate-key
'-iterate-least/>?
'-iterate-least/>=?
'-iterate-greatest/<?
'-iterate-greatest/<=?)))
(provide hash-test)
;; ============================================================
;; Performance tests
;; ============================================================
(define (p name testf mkd ordered?)
(let-values ([(_result cpu real gc)
(time-apply
(lambda ()
(for ([i (in-range 20)])
(testf (list (mkd)) ordered?)))
null)])
(printf "~a : ~s\n" name cpu)))
(define (mksplay) (make-splay-tree = <))
(define (mkadj) (make-adjustable-splay-tree))
(define (mkcsplay) (make-splay-tree = < #:key-contract number? #:value-contract number?))
(define (mkskip) (make-skip-list = <))
(define (mkcskip) (make-skip-list = < #:key-contract number? #:value-contract number?))
(define (performance)
(display "Using ordered-dict interface, w/ search\n")
(p "splay-tree" dict-test mksplay #t)
(p "adj-splay " dict-test mkadj #t)
(p "skip-list " dict-test mkskip #t)
(p "splay w/ c" dict-test mkcsplay #t)
(p "skip w/ c " dict-test mkcskip #t)
(newline)
(display "Using custom interfaces, w/ search\n")
(p "splay-tree" splay-test mksplay #t)
(p "adj-splay " splay-test mkadj #t)
(p "skip-list " skip-test mkskip #t)
(p "splay w/ c" splay-test mkcsplay #t)
(p "skip w/ c " skip-test mkcskip #t)
(newline)
(display "Using custom interfaces, w/o search\n")
(p "splay-tree" splay-test mksplay #f)
(p "adj-splay " splay-test mksplay #f)
(p "skip-list " skip-test mkskip #f)
(p "splay w/ c" splay-test mkcsplay #f)
(p "skip w/ c " skip-test mkcskip #f)
(p "hash " hash-test make-hash #f)
(newline))
(provide performance)
#|
Recent run:
Using ordered-dict interface, w/ search
splay-tree : 3745
adj-splay : 3640
skip-list : 3920
splay w/ c : 5884
skip w/ c : 6297
Using custom interfaces, w/ search
splay-tree : 3140
adj-splay : 3128
skip-list : 3428
splay w/ c : 3277
skip w/ c : 3624
Using custom interfaces, w/o search
splay-tree : 2732
adj-splay : 2736
skip-list : 2976
splay w/ c : 2809
skip w/ c : 3076
hash : 3456
Recent run with dict contracts bypassed (impl and client):
(Requires multiple changes to code, not easy to automate.)
Using ordered-dict interface, w/ search
splay-tree : 3368
adj-splay : 3472
skip-list : 3276
splay w/ c : 4729
skip w/ c : 5104
Conclusions:
- per-inst contracts using dict interface are expensive,
but per-inst contracts using custom interface not that bad
(why?)
(provide ud-test
cd-test)
|#