From: Tamas K Papp on
Hi,

I am considering migrating part of some code from classes to
structures. I am doing this because I read that structures can lead
to gains in speed in some cases. The code is mature now, and I expect
few changes. I am not using the MOP or anything fancy, so I guess
structs should work fine. I have a general and a specific question:

1. I would be interested in hearing about projects where a similar
change was undertaken, and whether it lead to significant speed
improvements (I am using SBCL, but the project will be portable, all
stories are welcome).

2. A specific question: I am wondering how to do consistency checks on
certain objects. Eg if I want to ensure that two slots are always
equal, for classes I would just have an INITIALIZE-INSTANCE :AFTER
method to check that. I came up with the following for structs:

(defstruct (foo (:constructor make-foo% (a b)))
;; make-foo% not exported
(a 0 :type fixnum :read-only t)
(b 0 :type fixnum :read-only t))

(defun make-foo (a b)
(assert (= a b))
(make-foo% a b))

Is there a simpler way?

Thanks,

Tamas
From: Stelian Ionescu on
On Fri, 21 May 2010 09:34:32 +0000, Tamas K Papp wrote:
> 2. A specific question: I am wondering how to do consistency checks on
> certain objects. Eg if I want to ensure that two slots are always
> equal, for classes I would just have an INITIALIZE-INSTANCE :AFTER
> method to check that. I came up with the following for structs:
>
> (defstruct (foo (:constructor make-foo% (a b)))
> ;; make-foo% not exported
> (a 0 :type fixnum :read-only t)
> (b 0 :type fixnum :read-only t))
>
> (defun make-foo (a b)
> (assert (= a b))
> (make-foo% a b))
>
> Is there a simpler way?

(defstruct (foo (:constructor %make-foo (a b)))
;; make-foo% not exported
(a 0 :type fixnum :read-only t)
(b 0 :type fixnum :read-only t))

(defun make-foo (a)
(make-foo% a a))

--
Stelian Ionescu a.k.a. fe[nl]ix
Quidquid latine dictum sit, altum videtur.
http://common-lisp.net/project/iolib
From: Thomas A. Russ on
Tamas K Papp <tkpapp(a)gmail.com> writes:

> Hi,
>
> I am considering migrating part of some code from classes to
> structures. I am doing this because I read that structures can lead
> to gains in speed in some cases. The code is mature now, and I expect
> few changes. I am not using the MOP or anything fancy, so I guess
> structs should work fine. I have a general and a specific question:

Of course, question one is whether your current code is fast enough? If
it is, then there might not be much in the way of gains to be worth
making the effort to switch.

> 1. I would be interested in hearing about projects where a similar
> change was undertaken, and whether it lead to significant speed
> improvements (I am using SBCL, but the project will be portable, all
> stories are welcome).

Our Stella system (and by extension PowerLoom) has the option of
producing either CLOS-based or STRUCT-based objects. We have found
that we can get a speed-up using the struct-based code that is between
1.5x and 2x that of the CLOS-based code.

This does cause some re-working of, in particular, the accesors, since
they are no longer generic functions but normal functions (well they
compile down to array accesses). That gives us a lot of the speed-up,
but it also means that one often has to know a bit more about exactly
what items one is accessing.

We do still use generic functions and methods to do operations other
than slot access on the class or struct instances, so our speedup really
comes from a combination of a (slightly) smaller memory footprint and
the use of more efficient accessors to get slot values.

> 2. A specific question: I am wondering how to do consistency checks on
> certain objects. Eg if I want to ensure that two slots are always
> equal, for classes I would just have an INITIALIZE-INSTANCE :AFTER
> method to check that. I came up with the following for structs:

Well, it seems like you really like some of the features that you can
get from CLOS, so unless you think the speedup is worth it, you may be
happier just staying with CLOS.
>
> (defstruct (foo (:constructor make-foo% (a b)))
> ;; make-foo% not exported
> (a 0 :type fixnum :read-only t)
> (b 0 :type fixnum :read-only t))
>
> (defun make-foo (a b)
> (assert (= a b))
> (make-foo% a b))
>
> Is there a simpler way?

Not really. Since you don't have the more sophisticated initialization
protocol of CLOS, you have to put all of that checking into your struct
constructor. You will still not have anything that maintains the
constraint about A and B remaining the same if one of them changes. You
can do that with SETF methods on the classes but you can't do that for
structs.

--
Thomas A. Russ, USC/Information Sciences Institute
From: Pascal J. Bourguignon on
Tamas K Papp <tkpapp(a)gmail.com> writes:

> Hi,
>
> I am considering migrating part of some code from classes to
> structures. I am doing this because I read that structures can lead
> to gains in speed in some cases. The code is mature now, and I expect
> few changes. I am not using the MOP or anything fancy, so I guess
> structs should work fine. I have a general and a specific question:
>
> 1. I would be interested in hearing about projects where a similar
> change was undertaken, and whether it lead to significant speed
> improvements (I am using SBCL, but the project will be portable, all
> stories are welcome).
>
> 2. A specific question: I am wondering how to do consistency checks on
> certain objects. Eg if I want to ensure that two slots are always
> equal, for classes I would just have an INITIALIZE-INSTANCE :AFTER
> method to check that. I came up with the following for structs:
>
> (defstruct (foo (:constructor make-foo% (a b)))
> ;; make-foo% not exported
> (a 0 :type fixnum :read-only t)
> (b 0 :type fixnum :read-only t))
>
> (defun make-foo (a b)
> (assert (= a b))
> (make-foo% a b))
>
> Is there a simpler way?

I recently wrote this macro:


(defmacro define-destructuring-structure (name &rest arguments)
(let ((docstring (when (and (rest arguments)
(stringp (first arguments)))
(pop arguments)))
(documented-lambda-list (pop arguments))
(checks arguments))
(flet ((documentation-map (documented-lambda-list)
;; FIXME: use source-form to parse the macro-lambda-list and retrieve the parameter names.
(mapcar (lambda (x) (list (first x) (second x)))
(remove '&key documented-lambda-list)))
(lambda-list (documented-lambda-list)
;; FIXME: use source-form to parse the macro-lambda-list and retrieve the parameter names.
(mapcar (lambda (x) (if (atom x) x (first x))) documented-lambda-list))
(parameter-names (lambda-list)
;; FIXME: use source-form to parse the macro-lambda-list and retrieve the parameter names.
(remove '&key lambda-list)))
(let* ((docstring-map (documentation-map documented-lambda-list))
(lambda-list (lambda-list documented-lambda-list))
(parameter-names (parameter-names lambda-list)))
`(progn
(defstruct ,name
,(format nil "~%A ~S structure contains the following fields:~:{~% ~30D ~A~}~%~@[~A~%~]"
name docstring-map docstring)
,@parameter-names)
(defun ,(conc-name 'parse name) (arguments)
,(format nil "Parse a ~S structure." name)
(destructuring-bind ,lambda-list arguments
(progn ,@check)
(,(conc-name 'make name) ,@(mapcan (lambda (pn) (list (keywordize pn) pn))
parameter-names))))
',name)))))


so that I can write things like:

(define-destructuring-structure point
"A little destructuring structure for an example."
((x "the X coordinate")
(y "the Y coordinate")
&key
(color "the color of the point")
(charm "the charm of the point"))
;; here come code inserted in the constructor.
(assert (imply (< x y) (redp color)))
(assert (imply (> x y) charm)))


(list
(parse-point 0 0 :color 'blue)
(parse-point 42 24 :charm t :color 'blue))


Playing with defstruct conc-name option could also be done.



But the point is that there is always a simplier way to write things,
as long as you are able to write the macro and functions needed to
implement this more concise language.

Also, I would advise to define your entities with your own set of
macros instead of defclass or defstruct, so that you can switch from
one to the other easily. I could easily change the defstruct in my
define-destructuring-macro into a defclass...


--
__Pascal Bourguignon__ http://www.informatimago.com/