You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, the Vector type is constructed as Common Lisp vectors. The CL vectors are specialized on the element type, which in principle gives us benefits in speed and memory, at the very tiny cost of carrying around a RuntimeRepr constraint in polymorphic code.
Since our vectors are by default adjustable with fill pointers, we don't actually fully realize a speed benefit. The assembly code for the following two functions is identical:
(declaim (optimize speed (safety 0) (debug 0)))
(defun sum1 (a)
(declare (type (vector t) a))
(loop :with s :of-type double-float := 0.0d0
:for x :of-type double-float :across a
:do (incf s x)
:finally (return s)))
(defun sum2 (a)
(declare (type (vector double-float) a))
(loop :with s :of-type double-float := 0.0d0
:for x :of-type double-float :across a
:do (incf s x)
:finally (return s)))
While we may not realize a speed benefit, we do get a storage benefit. Consider these three definitions:
(defun general-adjustable (n)
(let ((x (make-array n :element-type t :initial-element 1.0d0 :adjustable t :fill-pointer n)))
(declare (type (vector t) x))
(map-into x #'random x)
(length x)))
(defun special-adjustable (n)
(let ((x (make-array n :element-type 'double-float :initial-element 1.0d0 :adjustable t :fill-pointer n)))
(declare (type (vector (double-float (0.0d0))) x))
(map-into x #'random x)
(length x)))
(defun special-fixed (n)
(let ((x (make-array n :element-type 'double-float :initial-element 1.0d0)))
(declare (type (simple-array (double-float (0.0d0)) (*)) x))
(map-into x #'random x)
(length x)))
Metering them we get:
CL-USER> (time (general-adjustable 10000000))
Evaluation took:
0.219 seconds of real time
0.219447 seconds of total run time (0.201021 user, 0.018426 system)
100.00% CPU
526,860,812 processor cycles
239,985,216 bytes consed
10000000
CL-USER> (time (special-adjustable 10000000))
Evaluation took:
0.130 seconds of real time
0.130024 seconds of total run time (0.129971 user, 0.000053 system)
100.00% CPU
312,132,498 processor cycles
80,000,016 bytes consed
10000000
CL-USER> (time (special-fixed 10000000))
Evaluation took:
0.146 seconds of real time
0.146113 seconds of total run time (0.128996 user, 0.017117 system)
100.00% CPU
350,772,302 processor cycles
80,000,016 bytes consed
10000000
We see the latter two definitions consed less than 1/2 the memory.
However, while the objects themselves occupy less memory, we actually still cons a lot with our sumN functions:
CL-USER> (let ((x (general-adjustable 10000000)))
(time (sum1 x)))
Evaluation took:
0.046 seconds of real time
0.046030 seconds of total run time (0.046018 user, 0.000012 system)
100.00% CPU
110,467,390 processor cycles
0 bytes consed
5000138.483784734d0
CL-USER> (let ((x (special-adjustable 10000000)))
(time (sum2 x)))
Evaluation took:
0.176 seconds of real time
0.176989 seconds of total run time (0.164089 user, 0.012900 system)
[ Run times consist of 0.116 seconds GC time, and 0.061 seconds non-GC time. ]
100.57% CPU
423,857,834 processor cycles
160,006,144 bytes consed
5001112.489638757d0
CL-USER> (let ((x (special-fixed 10000000)))
(time (sum3 x)))
Evaluation took:
0.009 seconds of real time
0.009601 seconds of total run time (0.009598 user, 0.000003 system)
111.11% CPU
23,036,176 processor cycles
0 bytes consed
4999124.358925528d0
I don't understand why the general (vector t) case didn't cons. So actually, it's both 2x faster and conses less memory at runtime, despite storage costing more.
As such, I think for a standard library object, we should remove the specialization.
(In a follow-on, we should add APIs for specialized, fixed-sized arrays.)
The text was updated successfully, but these errors were encountered:
If we wanted our arrays to be more efficient, we'd need a mechanism to emit (cl:and (cl:vector <TYPE>) ...) when known. Using * essentially forces Lisp to select the broadest possible accessor function.
Currently, the
Vector
type is constructed as Common Lisp vectors. The CL vectors are specialized on the element type, which in principle gives us benefits in speed and memory, at the very tiny cost of carrying around aRuntimeRepr
constraint in polymorphic code.Since our vectors are by default adjustable with fill pointers, we don't actually fully realize a speed benefit. The assembly code for the following two functions is identical:
Both of them have to do a "hairy vector ref", which is an unoptimized general reference into the vector.
Compare to this third, which is very optimized.
While we may not realize a speed benefit, we do get a storage benefit. Consider these three definitions:
Metering them we get:
We see the latter two definitions consed less than 1/2 the memory.
However, while the objects themselves occupy less memory, we actually still cons a lot with our
sumN
functions:I don't understand why the general
(vector t)
case didn't cons. So actually, it's both 2x faster and conses less memory at runtime, despite storage costing more.As such, I think for a standard library object, we should remove the specialization.
(In a follow-on, we should add APIs for specialized, fixed-sized arrays.)
The text was updated successfully, but these errors were encountered: