Skip to content

Commit 360d29d

Browse files
committed
benchmarks
1 parent 702263f commit 360d29d

File tree

6 files changed

+114
-24
lines changed

6 files changed

+114
-24
lines changed

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ Get chars or strings at a position:
7474
(print (rope:substr-rope rope 10 15)))
7575
```
7676

77+
# Performance
78+
79+
Time to insert is a good measure since it splits and concatenates.
80+
This graph demonstrates O(log(n)) performance:
81+
82+
![Insert Benchmark](screenshots/insert-benchmark.png)
83+
7784
# Dev Utils
7885

7986
If you want to generate graphs as shown above, you will need to

dev/benchmark.lisp

+51-12
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,29 @@
66
;; ;; (sb-ext:restrict-compiler-policy 'space 0 0)
77
;; (sb-ext:restrict-compiler-policy 'safety 0 0))
88

9+
(defun random-rope (total-length)
10+
(with-open-file (source "/dev/urandom" :element-type '(unsigned-byte 8))
11+
(labels ((read-leaves (&optional acc (acc-length 0))
12+
(let* ((string* (make-array (min rope::*long-leaf* (- total-length acc-length))
13+
:element-type '(unsigned-byte 8)))
14+
(length (read-sequence string* source))
15+
(string (map 'string #'code-char string*))
16+
(leaf (rope::make-leaf (subseq string 0 length) length)))
17+
(if (and (= rope::*long-leaf* length)
18+
(not (= total-length (+ length acc-length))))
19+
(read-leaves (cons leaf acc) (+ length acc-length))
20+
(cons leaf acc)))))
21+
(let ((leaves (nreverse (read-leaves))))
22+
(rope::merge-leaves leaves 0 (length leaves))))))
923

1024
(defparameter *readme*
1125
(merge-pathnames "README.md" (asdf:system-source-directory :rope)))
1226

1327
(defun print-time (time reps length)
1428
(format t "rope size: ~a, ~a microseconds~%"
1529
length
16-
(* 1000000 (/ time reps))))
30+
(* 1000000 (/ time reps)))
31+
(force-output))
1732

1833
(defmacro time* (&body body)
1934
`(let ((time))
@@ -22,19 +37,43 @@
2237
(lambda () ,@body))
2338
(coerce (/ time 1000) 'float)))
2439

25-
(defun benchmark-insert (&optional (reps 1000000))
26-
(with-open-file (s *readme*)
27-
(let* ((starting-rope (split-rope (make-rope s) 1000))
28-
(rope starting-rope))
29-
(dotimes (i 9999)
30-
(setf rope (concat-rope rope starting-rope)))
31-
(setf starting-rope rope)
32-
(dotimes (i 100)
40+
(defun benchmark-size (size step times reps)
41+
(let ((rope (random-rope size))
42+
(step (random-rope step)))
43+
(dotimes (i times)
44+
(sb-ext:gc :full t)
45+
(print-time
46+
(time*
47+
(dotimes (i reps)
48+
(insert-rope rope (random (rope-length rope)) "Hello, world!")))
49+
reps
50+
(rope-length rope))
51+
(setf rope (concat-rope rope step)))))
52+
53+
(defun benchmark-log (start-size limit reps)
54+
(let ((rope (random-rope start-size))
55+
(step (random-rope start-size)))
56+
(dotimes (i limit)
57+
(dotimes (i 10)
58+
(unless (= i 0)
59+
(setf rope (insert-rope rope
60+
(random (rope-length rope))
61+
step)))
62+
(sb-ext:gc :full t)
3363
(print-time
3464
(time*
3565
(dotimes (i reps)
3666
(insert-rope rope (random (rope-length rope)) "Hello, world!")))
3767
reps
38-
(rope-length rope))
39-
(force-output)
40-
(setf rope (concat-rope rope starting-rope))))))
68+
(rope-length rope)))
69+
(setf step rope))))
70+
71+
(defun benchmark-insert (&optional (reps 1000000))
72+
(setf rope::*long-leaf* 512)
73+
(benchmark-log 1000 6 reps)
74+
;; (benchmark-size 10000 10000 10 reps)
75+
;; (benchmark-size 100000 10000 10 reps)
76+
;; (benchmark-size 1000000 100000 10 reps)
77+
;; (benchmark-size 10000000 1000000 10 reps)
78+
;; (benchmark-size 100000000 10000000 100 reps)
79+
)

dev/package.lisp

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
(defpackage #:rope/dev
22
(:use #:cl #:cl-dot #:rope)
3+
(:local-nicknames
4+
(#:a #:alexandria))
35
(:export
46
;; graphviz.lisp
57
#:graph-ropes

rope.asd

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
(assert (eql t stat)))))
2222

2323
(asdf:defsystem #:rope/dev
24-
:depends-on (#:cl-dot #:rope)
24+
:depends-on (#:alexandria #:cl-dot #:rope)
2525
:components ((:module "dev"
2626
:components ((:file "package")
2727
(:file "graphviz")

screenshots/insert-benchmark.png

150 KB
Loading

test/fuzz.lisp

+53-11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,29 @@
1111
:length (1- length)
1212
:acc (rope::strcat acc (string (a:random-elt *charset*))))))
1313

14+
(defun random-rope (total-length)
15+
(with-open-file (source "/dev/urandom" :element-type '(unsigned-byte 8))
16+
(labels ((read-leaves (&optional acc (acc-length 0))
17+
(let* ((string* (make-array (min rope::*long-leaf* (- total-length acc-length))
18+
:element-type '(unsigned-byte 8)))
19+
(length (read-sequence string* source))
20+
(string (map 'string #'code-char string*))
21+
(leaf (rope::make-leaf (subseq string 0 length) length)))
22+
(if (and (= rope::*long-leaf* length)
23+
(not (= total-length (+ length acc-length))))
24+
(read-leaves (cons leaf acc) (+ length acc-length))
25+
(cons leaf acc)))))
26+
(let ((leaves (nreverse (read-leaves))))
27+
(rope::merge-leaves leaves 0 (length leaves))))))
28+
29+
(defgeneric balancedp (rope)
30+
(:method ((rope rope::leaf))
31+
t)
32+
(:method ((rope rope::branch))
33+
(and (>= 1 (abs (rope::balance-factor rope)))
34+
(balancedp (rope::branch-left rope))
35+
(balancedp (rope::branch-right rope)))))
36+
1437
(deftest fuzz-basic-tests ()
1538
"Run the basic test suite with different leaf sizes."
1639
(loop :for i :from 1 :to 64
@@ -20,39 +43,58 @@
2043
(is (run-tests :rope/test/basic)))))
2144

2245
(deftest fuzz-split ()
23-
(dotimes (i 10)
46+
(dotimes (i 1000)
2447
(let* ((length (random 10000))
2548
(string (random-string :length length))
2649
(rope (rope:make-rope string))
2750
(index (random length)))
2851
(multiple-value-bind (ante post) (rope:split-rope rope index)
52+
(is (balancedp ante))
53+
(is (balancedp post))
2954
(is (string= (subseq string 0 index) (rope:write-rope ante nil)))
3055
(is (string= (subseq string index) (rope:write-rope post nil)))))))
3156

3257
(deftest fuzz-index ()
33-
(dotimes (i 100)
58+
(dotimes (i 1000)
3459
(let* ((length (1+ (random 1000)))
3560
(string (random-string :length length))
3661
(rope (rope:make-rope string))
3762
(index (random length)))
3863
(is (char= (char string index) (rope:index-rope rope index))))))
3964

4065
(deftest fuzz-concat ()
41-
(dotimes (i 100)
66+
(dotimes (i 1000)
4267
(let* ((string-a (random-string :length (random 1000)))
4368
(string-b (random-string :length (random 1000)))
4469
(rope-a (rope:make-rope string-a))
45-
(rope-b (rope:make-rope string-b)))
46-
(is (string= (rope::strcat string-a string-b)
47-
(rope:write-rope (rope:concat-rope rope-a rope-b) nil))))))
70+
(rope-b (rope:make-rope string-b))
71+
(new-string (rope::strcat string-a string-b))
72+
(new-rope (rope:concat-rope rope-a rope-b)))
73+
(is (balancedp new-rope))
74+
(is (string= new-string (rope:write-rope new-rope nil))))))
4875

4976
(deftest fuzz-kill ()
50-
(dotimes (i 10000)
77+
(dotimes (i 1000)
5178
(let* ((length (+ 10 (random 1000)))
5279
(string (random-string :length length))
5380
(rope (rope:make-rope string))
5481
(end (1+ (random (1- length))))
55-
(start (random end)))
56-
(is (string= (rope::strcat (subseq string 0 start)
57-
(subseq string end))
58-
(rope:write-rope (rope:kill-rope rope start end) nil))))))
82+
(start (random end))
83+
(new-string (rope::strcat (subseq string 0 start) (subseq string end)))
84+
(new-rope (rope:kill-rope rope start end)))
85+
(is (balancedp new-rope))
86+
(is (string= new-string (rope:write-rope new-rope nil))))))
87+
88+
#+ignore
89+
(deftest fuzz-insert-balance ()
90+
(setf rope::*long-leaf* 128)
91+
(dotimes (i 10)
92+
(let ((rope (random-rope 1000)))
93+
(dotimes (i 100)
94+
(setf rope
95+
(rope:insert-rope rope
96+
(random (rope:rope-length rope))
97+
(random-string :length (random 512))))
98+
(unless (balancedp rope)
99+
(return-from fuzz-insert-balance rope))
100+
(is (balancedp rope))))))

0 commit comments

Comments
 (0)