Class VS Struct in Lisp Revisited

Last time we tried to test Class VS Struct, and I got several comments on ways I could improve the tests. We will be looking at performance improvements that can be made for defstruct. We will be running on an Intel Core i5 Pixelbook using SBCL (except where CCL is specified).

You can find the code:

https://github.com/Slids/lisp-perf-test/blob/master/class-v-struct-boa.lisp


Random Takes Time

Starting, random adds quite a bit of time. It used enough time to massively obfuscate the time things were taking. We can see how much time random is taking

(time (dotimes (i 1000000000) (random 100)))

Evaluation took:
31.172 seconds of real time
31.168020 seconds of total run time (31.168000 user, 0.000020 system)
99.99% CPU
50,123,948,880 processor cycles
0 bytes consed


Argument Constructors are Bad

The next problem is using the default struct constructor instead of using a a constructor with positional arguments. I say, why should this by slower? They say, it’s a bug. We see:

(defstruct (simple-struct
             (:constructor make-simple-struct
               (slot-1 slot-2 slot-3)))
             (slot-1)
             (slot-2)
             (slot-3))
(time (dotimes (i 1000000000)
        (make-simple-struct 1 2 3)))

Evaluation took:
9.602 seconds of real time
9.714198 seconds of total run time (9.268526 user, 0.445672 system)
[ Run times consist of 2.013 seconds GC time, and 7.702 seconds non-GC time. ]
101.17% CPU
15,438,933,331 processor cycles
32,000,006,848 bytes consed

(defstruct simple-struct-2
  (slot-1)
  (slot-2)
  (slot-3))
(time (dotimes (i 1000000000)
        (make-simple-struct-2 :slot-1 1
                              :slot-2 2
                              :slot-3 3)))

Evaluation took:
20.735 seconds of real time
20.927129 seconds of total run time (20.470935 user, 0.456194 system)
[ Run times consist of 2.453 seconds GC time, and 18.475 seconds non-GC time. ]
100.93% CPU
33,340,558,882 processor cycles
32,000,007,568 bytes consed

So we get pretty large savings making a boa-constructor!

We can do more and declaim the constructor inline:

(declaim (inline make-simple-struct-3))
(defstruct (simple-struct-3
             (:constructor make-simple-struct-3
               (slot-1 slot-2 slot-3)))
             (slot-1)
             (slot-2)
             (slot-3))
(time (dotimes (i 1000000000)
        (make-simple-struct-3 1 2 3)))

Evaluation took:
0.363 seconds of real time
0.362868 seconds of total run time (0.362868 user, 0.000000 system)
100.00% CPU
583,539,405 processor cycles
0 bytes consed

We were able to stack-allocate the struct leading to quite the savings and no garbage collection. I wouldn’t suggest this very often but it’s an impressive change.


Rerunning Instantiation Tests

I will re-run the instantiation test with our defstruct having the boa-constructor and not using random.

SBCL

CCL.png

Note: For CCL the struct instantiation test took: 0.238095 seconds. Why was class so terrible? CCL folks?

For there information the output was:

(LOOP FOR I FROM 1 TO RUN-TIMES DO (MAKE-INSTANCE ‘SIMPLE-CLASS :SLOT-1 I :SLOT-2 I :SLOT-3 I))
took 33,453,955 microseconds (33.453957 seconds) to run.
68,225 microseconds ( 0.068225 seconds, 0.20%) of which was spent in GC.
During that period, and with 4 available CPU cores,
33,421,986 microseconds (33.421986 seconds) were spent in user mode
66,531 microseconds ( 0.066531 seconds) were spent in system mode
800,000,528 bytes of memory allocated.
303 minor page faults, 0 major page faults, 0 swaps.

Thanks

I would like to thank @dougk (Google) and @stassats (Reddit account) for the comments and suggestions!


As usual I like to end with a picture:

img_20190909_174425

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s