Skip to content

Commit 9301a58

Browse files
committed
Added somewhat faster store / restore on specialized vectors for
non-sbcl lisps. Introduce shifted interior fixnum encoding to save more space Fixed long string bugs Bugfixes for allegro
1 parent c502935 commit 9301a58

12 files changed

+495
-195
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ git clone the repo into your local quicklisp directory (usually ~/quicklisp/loca
6565
CL-USER> (quicklisp:quickload "cl-binary-store")
6666
CL-USER> (in-package :cl-binary-store-user) ; or (use-package :cl-binary-store-user)
6767
CL-BINARY-STORE-USER> (store nil (list "abcd" 1234))
68-
#(32 210 30 212 97 98 99 100 1 210 4 5)
69-
;; 32 = proper list, 210 = tiny integer length 2, 30 = simple-string, 212 = tiny integer length 4, 97 98 99 100 = abcd, 4 = cons 1 210 4 = 16 bit integer 1234, 5 = nil
68+
#(32 6 30 8 97 98 99 100 1 210 4 5)
69+
;; 32 = proper list, 6 = integer length 2, 30 = simple-string, 8 = integer length 4, 97 98 99 100 = abcd, 4 = cons 1 210 4 = 16 bit integer 1234, 5 = nil
7070

7171
CL-BINARY-STORE-USER> (restore (store nil (list "abcd" 1234)))
7272
("abcd" 1234)
@@ -76,7 +76,7 @@ git clone the repo into your local quicklisp directory (usually ~/quicklisp/loca
7676
CL-BINARY-STORE-USER> (restore "blarg.bin")
7777
HI
7878
CL-BINARY-STORE-USER> (store nil (make-string 1 :initial-element #\U+03b1))
79-
#(30 210 206 177) ;; 31 is simple-string, 210 encoded length = 2, 206 117 = α
79+
#(30 6 206 177) ;; 31 is simple-string, 6 encoded length = 2, 206 117 = α
8080

8181
CL-BINARY-STORE-USER> (let* ((*print-circle* t)
8282
(u (make-array 1))

benchmarking.md

+20
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,26 @@ Here are simple-bit-vectors, one of my favorite data structures in Common Lisp
223223
WRITE: 664.40 ms at 60 MB/sec
224224
READ : 504.80 ms at 79 MB/sec
225225

226+
Just all the specialized vectors I can think of. It goes fast on sbcl.
227+
228+
CL-BINARY-STORE> (test-on-data (a-bunch-of-specialized-arrays 100000))
229+
HYPERLUMINAL-MEM
230+
OUTPUT SIZE: 12.81 MB
231+
WRITE: 11.04 ms at 1161 MB/sec
232+
READ : 9.64 ms at 1329 MB/sec
233+
CL-BINARY-STORE
234+
OUTPUT SIZE: 5.09 MB
235+
WRITE: 0.32 ms at 15899 MB/sec
236+
READ : 0.64 ms at 7949 MB/sec
237+
CL-CONSPACK
238+
OUTPUT SIZE: 5.70MB
239+
WRITE: 58.00 ms at 98 MB/sec
240+
READ : 47.20 ms at 121 MB/sec
241+
CL-STORE
242+
OUTPUT SIZE: 20.10MB
243+
WRITE: 338.80 ms at 59 MB/sec
244+
READ : 222.00 ms at 91 MB/sec
245+
226246
The rest of the story is the same here, for specialized vectors and specialized arrays we are very very fast because we are just blitting them. This means cl-binary-store is a good choice for serializing double float matrices, for example:
227247

228248
CL-BINARY-STORE> (test-on-data (list-of-double-float-matrices))

benchmarks.lisp

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(quicklisp:quickload "cl-store")
2-
#-allegro(quicklisp:quickload "hyperluminal-mem")
2+
#-(or lispworks allegro)(quicklisp:quickload "hyperluminal-mem")
33
(quicklisp:quickload "cl-conspack")
44
#+sbcl (require 'sb-sprof)
55

@@ -21,7 +21,7 @@
2121
"")))))))
2222

2323

24-
#-(or allegro abcl) ;; crashes on abcl
24+
#-(or allegro abcl lispworks) ;; crashes on abcl
2525
(defun test-hlmem-on-data (data &key (repeats 100))
2626
(let* ((words (hyperluminal-mem:msize 0 data))
2727
(output-size (/ (* 8 words) 1e6)))
@@ -130,7 +130,7 @@
130130
(dotimes (x repeats) (cl-store:restore "blarg.bin")))))))
131131

132132
(defun test-on-data (data &key (hlmem t) (cl-store t) (cl-binary-store t) (conspack t))
133-
#-(or allegro abcl)
133+
#-(or allegro abcl lispworks)
134134
(when hlmem
135135
(test-hlmem-on-data data))
136136
(when cl-binary-store
@@ -295,6 +295,27 @@
295295
(svref a (random number))))
296296
a))
297297

298+
(defun a-bunch-of-specialized-arrays (&optional (n 10000))
299+
(loop for type in '((unsigned-byte 1)
300+
(unsigned-byte 2)
301+
(unsigned-byte 4)
302+
(unsigned-byte 8)
303+
(unsigned-byte 16)
304+
(unsigned-byte 32)
305+
fixnum
306+
(unsigned-byte 64)
307+
(signed-byte 8)
308+
(signed-byte 16)
309+
(signed-byte 32)
310+
(signed-byte 64)
311+
single-float
312+
double-float)
313+
for elt in (list 1 2 15 255 65535 (1- (expt 2 32)) (expt 2 50) (1- (expt 2 64))
314+
-1 -128 -32768 -100000 1f0 -1d0)
315+
collect
316+
(make-array n :element-type type :initial-element elt)))
317+
318+
298319
(defstruct address
299320
(street "Ave Greene" :type simple-string)
300321
(state "QC" :type simple-string)

src/basic-codespace-codes.lisp

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
(defconstant +ub16-code+ 1)
1313
(defconstant +ub32-code+ 2)
1414
(defconstant +fixnum-code+ 3)
15+
(defconstant +first-direct-unsigned-integer-interior-code+ 4)
16+
(defconstant +interior-coded-max-integer+ (- 255 +first-direct-unsigned-integer-interior-code+))
17+
;; Inside of another tag region, when encoding an unsigned integer, 0
18+
;; - 3 is a tag for an extended number, and 4-255 are direct coded
19+
;; lengths. If 0-3 is used, then the number encoded is shifted down
20+
;; by +interior-coded-max-integer+.
1521
(defconstant +cons-code+ 4)
1622
(defconstant +nil-code+ 5)
1723
(defconstant +sb8-code+ 6)
@@ -68,3 +74,4 @@
6874
+small-integer-zero-code+))
6975
(defconstant +minimum-untagged-signed-integer+ (- +first-small-integer-code+
7076
+small-integer-zero-code+))
77+

src/basic-codespace.lisp

-2
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,7 @@
141141
(restore-new-reference-indicator references restore-object))
142142

143143
;; SIMPLE VECTORS
144-
#+sbcl
145144
(defstore (simple-array * (*)) (store-simple-specialized-vector obj storage) :check-for-ref-in eq-refs)
146-
#+sbcl
147145
(defrestore +simple-specialized-vector-code+ (restore-simple-specialized-vector storage))
148146

149147
(defstore simple-vector (store-simple-vector obj storage store-object) :check-for-ref-in eq-refs)

src/cons.lisp

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
(defun store-cons/finite (cons storage eq-refs store-object assign-new-reference-id list-lengths)
4545
"This is called during the actual storage output phase if we have already computed the list
4646
length. This is not called when *support-shared-list-structures* is true."
47-
(declare (optimize (speed 3) (safety 0))
47+
(declare (optimize (speed 3) (safety 3) (debug 3))
4848
(type write-storage storage) (type function store-object))
4949
(maybe-store-reference-instead (cons storage eq-refs assign-new-reference-id)
5050
(let ((length (or (and list-lengths (gethash cons list-lengths))
@@ -53,7 +53,7 @@
5353
(declare (type fixnum length))
5454
(with-write-storage (storage :offset offset :reserve-bytes 1 :sap sap)
5555
(set-sap-ref-8 sap offset +finite-length-list-code+))
56-
(store-tagged-unsigned-fixnum length storage)
56+
(store-tagged-unsigned-fixnum/interior length storage)
5757
(dotimes (count length)
5858
(cond
5959
((= count (- length 1))
@@ -141,7 +141,7 @@
141141
(declaim (inline restore-list/known-length))
142142
(defun restore-list/known-length (storage restore-object)
143143
(declare (optimize (speed 3) (safety 0)))
144-
(let* ((length (restore-tagged-unsigned-fixnum storage)))
144+
(let* ((length (restore-tagged-unsigned-fixnum/interior storage)))
145145
(let* ((head (make-list length))
146146
(cons head))
147147
(dotimes (count (1- length))

src/numbers.lisp

+36-2
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,41 @@
253253
(store-ub32 fixnum storage)
254254
(store-only-fixnum fixnum storage))))))
255255

256+
(declaim (inline store-tagged-unsigned-fixnum/interior))
257+
(defun store-tagged-unsigned-fixnum/interior (fixnum storage)
258+
"Use this paired with restore-tagged-unsigned-fixnum if you are inside another tagged
259+
region for storing unsigned numbers. Somewhat more dense as we only need tags 0-3 for
260+
tagging unsigned numbers."
261+
(declare (type fixnum fixnum) (type write-storage storage))
262+
(if (<= fixnum +interior-coded-max-integer+)
263+
(store-ub8/no-tag (+ +first-direct-unsigned-integer-interior-code+ fixnum)
264+
storage) ;; direct coded
265+
(let ((fixnum (- fixnum +interior-coded-max-integer+ 1))) ;; code shifted
266+
(if (< fixnum 256)
267+
(store-ub8/tag (truly-the fixnum fixnum) storage)
268+
(if (< fixnum 65536)
269+
(store-ub16 fixnum storage)
270+
(if (< fixnum #.(expt 2 32))
271+
(store-ub32 fixnum storage)
272+
(store-only-fixnum fixnum storage)))))))
273+
274+
275+
(declaim (inline restore-tagged-unsigned-fixnum/interior))
276+
(defun restore-tagged-unsigned-fixnum/interior (storage)
277+
"Use this if you know that this is an unsigned number (so after
278+
another tag bit). This opens up the direct coding space for up to
279+
+interior-coded-max-integer+."
280+
(declare (type read-storage storage))
281+
(let ((tag (restore-ub8 storage)))
282+
(if (> tag +first-direct-unsigned-integer-interior-code+)
283+
(- tag +first-direct-unsigned-integer-interior-code+)
284+
(+ (ecase tag
285+
(#.+ub8-code+ (restore-ub8 storage))
286+
(#.+ub16-code+ (restore-ub16 storage))
287+
(#.+ub32-code+ (restore-ub32 storage))
288+
(#.+fixnum-code+ (restore-fixnum storage)))
289+
+interior-coded-max-integer+ 1))))
290+
256291
(declaim (ftype (function (read-storage)
257292
#+sbcl (values fixnum &optional)
258293
#-sbcl fixnum)
@@ -261,7 +296,6 @@
261296
"Read back a number written by `store-tagged-unsigned-fixnum'."
262297
(declare (optimize (speed 3) (safety 1)))
263298
(let ((tag (restore-ub8 storage)))
264-
(declare (type (unsigned-byte 8) tag))
265299
(if (<= +small-integer-zero-code+ tag +last-small-integer-code+)
266300
(- tag +small-integer-zero-code+)
267301
(ecase tag
@@ -289,7 +323,7 @@
289323

290324
(declaim (inline store-fixnum))
291325
(defun store-fixnum (fixnum storage)
292-
"Store and tag a fixnum; "
326+
"Store and tag a fixnum; if inside another tag"
293327
(declare (type fixnum fixnum) (optimize (speed 3) (safety 1)) (type write-storage storage))
294328
(if (>= fixnum 0)
295329
(store-tagged-unsigned-fixnum fixnum storage)

src/reference-coding.lisp

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"Number ranges from -16 to wherever. This uses the reference-tag byte plus the tagged integer
3939
which can be anywhere from 1 byte direct tagged to arbitrarily large."
4040
(declare (optimize (speed 3) (safety 1)) (type fixnum number))
41-
(truly-the fixnum
41+
(the fixnum
4242
(+ (- +minimum-untagged-signed-integer+) number 1 +reference-two-byte-max-ref-id+)))
4343

4444
(declaim (inline encode-reference-direct))

src/sap-ref.lisp

+63
Original file line numberDiff line numberDiff line change
@@ -134,17 +134,80 @@
134134
(alexandria:once-only (x)
135135
`(logior ,x (- (mask-field (byte 1 (1- ,size)) ,x)))))
136136

137+
(defmacro negative-to-twos-complement/8 (x)
138+
(alexandria:once-only (x)
139+
`(progn
140+
(assert (< ,x 0))
141+
(logand (+ 1 (logxor (- ,x) #xFF)) #xFF))))
142+
143+
(defmacro negative-to-twos-complement/16 (x)
144+
(alexandria:once-only (x)
145+
`(progn
146+
(assert (< ,x 0))
147+
(logand (+ 1 (logxor (- ,x) #xFFFF)) #xFFFF))))
148+
149+
(defmacro negative-to-twos-complement/32 (x)
150+
(alexandria:once-only (x)
151+
`(progn
152+
(assert (< ,x 0))
153+
(logand (+ 1 (logxor (- ,x) #xFFFFFFFF)) #xFFFFFFFF))))
154+
137155
(defmacro negative-to-twos-complement/64 (x)
138156
(alexandria:once-only (x)
139157
`(progn
140158
(assert (< ,x 0))
141159
(logand (+ 1 (logxor (- ,x) #xFFFFFFFFFFFFFFFF)) #xFFFFFFFFFFFFFFFF))))
142160

161+
(defmacro signed-sap-ref-8 (sap offset)
162+
#+sbcl `(sb-sys:signed-sap-ref-8 ,sap ,offset)
163+
#-(or sbcl allegro) `(cffi:mem-ref ,sap :int8 ,offset)
164+
#+allegro `(mask-signed (sap-ref-8 ,sap ,offset) 8))
165+
166+
(defmacro signed-sap-ref-16 (sap offset)
167+
#+sbcl `(sb-sys:signed-sap-ref-16 ,sap ,offset)
168+
#-(or sbcl allegro) `(cffi:mem-ref ,sap :int16 ,offset)
169+
#+allegro `(mask-signed (sap-ref-16 ,sap ,offset) 16))
170+
171+
(defmacro signed-sap-ref-32 (sap offset)
172+
#+sbcl `(sb-sys:signed-sap-ref-32 ,sap ,offset)
173+
#-(or sbcl allegro) `(cffi:mem-ref ,sap :int32 ,offset)
174+
#+allegro `(mask-signed (sap-ref-32 ,sap ,offset) 32))
175+
143176
(defmacro signed-sap-ref-64 (sap offset)
144177
#+sbcl `(sb-sys:signed-sap-ref-64 ,sap ,offset)
145178
#-(or sbcl allegro) `(cffi:mem-ref ,sap :int64 ,offset)
146179
#+allegro `(mask-signed (sap-ref-64 ,sap ,offset) 64))
147180

181+
(defmacro set-signed-sap-ref-8 (sap offset value)
182+
#+sbcl `(setf (sb-sys:signed-sap-ref-8 ,sap ,offset) ,value)
183+
#-(or sbcl allegro) `(setf (cffi:mem-ref ,sap :int8 ,offset) ,value)
184+
#+allegro
185+
(alexandria:once-only (sap offset value)
186+
` (set-sap-ref-8 ,sap ,offset
187+
(if (< ,value 0)
188+
(negative-to-twos-complement/8 ,value)
189+
,value))))
190+
191+
(defmacro set-signed-sap-ref-16 (sap offset value)
192+
#+sbcl `(setf (sb-sys:signed-sap-ref-16 ,sap ,offset) ,value)
193+
#-(or sbcl allegro) `(setf (cffi:mem-ref ,sap :int16 ,offset) ,value)
194+
#+allegro
195+
(alexandria:once-only (sap offset value)
196+
` (set-sap-ref-16 ,sap ,offset
197+
(if (< ,value 0)
198+
(negative-to-twos-complement/16 ,value)
199+
,value))))
200+
201+
(defmacro set-signed-sap-ref-32 (sap offset value)
202+
#+sbcl `(setf (sb-sys:signed-sap-ref-32 ,sap ,offset) ,value)
203+
#-(or sbcl allegro) `(setf (cffi:mem-ref ,sap :int32 ,offset) ,value)
204+
#+allegro
205+
(alexandria:once-only (sap offset value)
206+
` (set-sap-ref-32 ,sap ,offset
207+
(if (< ,value 0)
208+
(negative-to-twos-complement/32 ,value)
209+
,value))))
210+
148211
(defmacro set-signed-sap-ref-64 (sap offset value)
149212
#+sbcl `(setf (sb-sys:signed-sap-ref-64 ,sap ,offset) ,value)
150213
#-(or sbcl allegro) `(setf (cffi:mem-ref ,sap :int64 ,offset) ,value)

0 commit comments

Comments
 (0)