[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

performance of arrays

Taoufik Dachraoui

1/13/2016 8:49:00 PM

Hi

(defun (i j v)
(let ((a (make-array (2000 2000))))
(incf (aref a i j) v)))

is much slower than:

(defun (i j v)
(declare (double-foat v))
(let ((a (make-array (2000 2000) :element-type 'double-float)))
(incf (aref a i j) v)))

How the implementation is storing the elements of the array
is different with and without declaration (I think that the elements
in the second function are stored unboxed in the array.

I would like to do the same in my application where
I use a tree of conses where the leafs are double-floats;

Is it possible to store conses with the car and cdr declared as double-floats
in order to avoid boxing; I tried few things without success.

Kind regards
Taoufik
13 Answers

Dimitri Fontaine

1/13/2016 9:12:00 PM

0

Taoufik Dachraoui <dachraoui.taoufik@gmail.com> writes:
> Is it possible to store conses with the car and cdr declared as double-floats
> in order to avoid boxing; I tried few things without success.

Did you try to use a defstruct for the tree leaves (and nodes maybe
too)? You could use :type for the structure slots.

--
dim

Barry Margolin

1/13/2016 10:05:00 PM

0

In article <11aa4c0a-dac1-4327-91d0-d57422fc46aa@googlegroups.com>,
Taoufik Dachraoui <dachraoui.taoufik@gmail.com> wrote:

> Is it possible to store conses with the car and cdr declared as double-floats
> in order to avoid boxing; I tried few things without success.

Theoretically declaring a variable as type (CONS DOUBLE-FLOAT
DOUBLE-FLOAT) would allow the implementation to unbox the car and cdr.
But I don't think there are any implementations that do so. It would
require extra overhead in every cons to indicate whether it's boxed or
unboxed, and every car/cdr call would have to check it (unless you use
the appropriate declaration everywhere the cons is accessed). This
overhead is considered acceptable for arrays, which are already
relatively complex objects. Conses are intended to be very efficient, so
complicating them is not a good idea.

--
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***

Taoufik Dachraoui

1/13/2016 10:14:00 PM

0

On Wednesday, January 13, 2016 at 10:12:14 PM UTC+1, Dimitri Fontaine wrote:
> Taoufik Dachraoui <dachraoui.taoufik@gmail.com> writes:
> > Is it possible to store conses with the car and cdr declared as double-floats
> > in order to avoid boxing; I tried few things without success.
>
> Did you try to use a defstruct for the tree leaves (and nodes maybe
> too)? You could use :type for the structure slots.
>
> --
> dim

I did the following test to compare conses and defstruct:

(defstruct mytree (left) (right))
(defun bar (n &optional ret)
(let ((a (make-mytree)))
(labels ((%bar (a m &optional rest)
(cond
((= m 0)
(setf (mytree-left a) 1.0D0)
(setf (mytree-right a) 1.0D0)
(if (not (null rest))
(%bar (setf (mytree-right (caar rest)) (make-mytree)) (cdar rest) (cdr rest))))
(t
(%bar (setf (mytree-left a) (make-mytree)) (1- m) (cons (cons a (1- m)) rest))))))
(%bar a n))
(if ret a)))

(defun jar (n &optional ret)
(let ((a (cons nil nil)))
(labels ((%bar (a m &optional rest)
(cond
((= m 0)
(setf (car a) 1.0D0)
(setf (cdr a) 1.0D0)
(if (not (null rest))
(%bar (setf (cdr (caar rest)) (cons nil nil)) (cdar rest) (cdr rest))))
(t
(%bar (setf (car a) (cons nil nil)) (1- m) (cons (cons a (1- m)) rest))))))
(%bar a n))
(if ret a)))

(defun bar-sum (a)
(cond
((mytree-p a)
(+ (bar-sum (mytree-left a)) (bar-sum (mytree-right a))))
(t a)))

(defun jar-sum (a)
(cond
((consp a)
(+ (jar-sum (car a)) (jar-sum (cdr a))))
(t a)))

? (time (bar 20)) ; test for defstruct
(BAR 20)
took 4,508 milliseconds (4.508 seconds) to run.
4,361 milliseconds (4.361 seconds, 96.74%) of which was spent in GC.
During that period, and with 2 available CPU cores,
4,452 milliseconds (4.452 seconds) were spent in user mode
39 milliseconds (0.039 seconds) were spent in system mode
50,331,616 bytes of memory allocated.
368 minor page faults, 0 major page faults, 0 swaps.
T
? (time (jar 20)) ; test for conses
(JAR 20)
took 2,255 milliseconds (2.255 seconds) to run.
2,149 milliseconds (2.149 seconds, 95.30%) of which was spent in GC.
During that period, and with 2 available CPU cores,
2,204 milliseconds (2.204 seconds) were spent in user mode
41 milliseconds (0.041 seconds) were spent in system mode
33,554,408 bytes of memory allocated.
3,664 minor page faults, 0 major page faults, 0 swaps.

? (time (bar-sum (bar 20 t))) ; ; test for defstruct
(BAR-SUM (BAR 20 T))
took 5,650 milliseconds (5.650 seconds) to run.
5,309 milliseconds (5.309 seconds, 93.96%) of which was spent in GC.
During that period, and with 2 available CPU cores,
4,673 milliseconds (4.673 seconds) were spent in user mode
110 milliseconds (0.110 seconds) were spent in system mode
83,886,032 bytes of memory allocated.
4,360 minor page faults, 0 major page faults, 0 swaps.
2097152.0D0

? (time (jar-sum (jar 20 t))) ; test for conses
(JAR-SUM (JAR 20 T))
took 2,797 milliseconds (2.797 seconds) to run.
2,564 milliseconds (2.564 seconds, 91.67%) of which was spent in GC.
During that period, and with 2 available CPU cores,
2,307 milliseconds (2.307 seconds) were spent in user mode
72 milliseconds (0.072 seconds) were spent in system mode
67,108,824 bytes of memory allocated.
2,496 minor page faults, 0 major page faults, 0 swaps.
2097152.0D0
?

Kind regards
Taoufik

Taoufik Dachraoui

1/13/2016 10:24:00 PM

0

On Wednesday, January 13, 2016 at 11:05:23 PM UTC+1, Barry Margolin wrote:
> In article <11aa4c0a-dac1-4327-91d0-d57422fc46aa@googlegroups.com>,
> Taoufik Dachraoui <dachraoui.taoufik@gmail.com> wrote:
>
> > Is it possible to store conses with the car and cdr declared as double-floats
> > in order to avoid boxing; I tried few things without success.
>
> Theoretically declaring a variable as type (CONS DOUBLE-FLOAT
> DOUBLE-FLOAT) would allow the implementation to unbox the car and cdr.
> But I don't think there are any implementations that do so. It would
> require extra overhead in every cons to indicate whether it's boxed or
> unboxed, and every car/cdr call would have to check it (unless you use
> the appropriate declaration everywhere the cons is accessed). This
> overhead is considered acceptable for arrays, which are already
> relatively complex objects. Conses are intended to be very efficient, so
> complicating them is not a good idea.
>
> --
> Barry Margolin, barmar@alum.mit.edu
> Arlington, MA
> *** PLEASE post questions in newsgroups, not directly to me ***

I am implementing a sparse array; is there a way to force the unboxing (in CCL?)

what about using a foreign object (implementing a sort of foreign cons)?

Kind regards
Taoufik

Barry Margolin

1/13/2016 11:54:00 PM

0

In article <51ed6bf8-09af-4c04-889f-83f4650bb662@googlegroups.com>,
Taoufik Dachraoui <dachraoui.taoufik@gmail.com> wrote:

> On Wednesday, January 13, 2016 at 10:12:14 PM UTC+1, Dimitri Fontaine wrote:
> > Taoufik Dachraoui <dachraoui.taoufik@gmail.com> writes:
> > > Is it possible to store conses with the car and cdr declared as
> > > double-floats
> > > in order to avoid boxing; I tried few things without success.
> >
> > Did you try to use a defstruct for the tree leaves (and nodes maybe
> > too)? You could use :type for the structure slots.
> >
> > --
> > dim
>
> I did the following test to compare conses and defstruct:

The suggestion was to use the :TYPE option in DEFSTRUCT, so it might
unbox the slots. You didn't do that.

> (BAR 20)
>
>
> took 4,508 milliseconds (4.508 seconds) to run.
>
>
> 4,361 milliseconds (4.361 seconds, 96.74%) of which was spent in GC.

> ? (time (jar 20)) ; test for conses
>
>
> (JAR 20)
>
>
> took 2,255 milliseconds (2.255 seconds) to run.
>
>
> 2,149 milliseconds (2.149 seconds, 95.30%) of which was spent in GC.

>
> ? (time (bar-sum (bar 20 t))) ; ; test for defstruct
>
>
> (BAR-SUM (BAR 20 T))
>
>
> took 5,650 milliseconds (5.650 seconds) to run.
>
>
> 5,309 milliseconds (5.309 seconds, 93.96%) of which was spent in GC.

> ? (time (jar-sum (jar 20 t))) ; test for conses
> (JAR-SUM (JAR 20 T))
> took 2,797 milliseconds (2.797 seconds) to run.
> 2,564 milliseconds (2.564 seconds, 91.67%) of which was spent in GC.

So in all your tests, almost all the time was spent in GC, not boxing
and unboxing.

--
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***

Günther Thomsen

1/14/2016 12:52:00 AM

0

On Wednesday, January 13, 2016 at 12:49:08 PM UTC-8, Taoufik Dachraoui wrote:
> Hi
>
> (defun (i j v)
> (let ((a (make-array (2000 2000))))
> (incf (aref a i j) v)))
>
> is much slower than:
>
> (defun (i j v)
> (declare (double-foat v))
> (let ((a (make-array (2000 2000) :element-type 'double-float)))
> (incf (aref a i j) v)))
>
> How the implementation is storing the elements of the array
> is different with and without declaration (I think that the elements
> in the second function are stored unboxed in the array.
>
> I would like to do the same in my application where
> I use a tree of conses where the leafs are double-floats;
>
> Is it possible to store conses with the car and cdr declared as double-floats
> in order to avoid boxing; I tried few things without success.
>
> Kind regards
> Taoufik

Isn't it common practice (at least outside the Lisp community) to implement trees (or other graphs) as arrays? Then you could have two arrays, one with the data (as double-floats) one with (fixnum) indices.

Taoufik Dachraoui

1/14/2016 7:57:00 AM

0

On Thursday, January 14, 2016 at 1:51:39 AM UTC+1, Günther Thomsen wrote:
> On Wednesday, January 13, 2016 at 12:49:08 PM UTC-8, Taoufik Dachraoui wrote:
> > Hi
> >
> > (defun (i j v)
> > (let ((a (make-array (2000 2000))))
> > (incf (aref a i j) v)))
> >
> > is much slower than:
> >
> > (defun (i j v)
> > (declare (double-foat v))
> > (let ((a (make-array (2000 2000) :element-type 'double-float)))
> > (incf (aref a i j) v)))
> >
> > How the implementation is storing the elements of the array
> > is different with and without declaration (I think that the elements
> > in the second function are stored unboxed in the array.
> >
> > I would like to do the same in my application where
> > I use a tree of conses where the leafs are double-floats;
> >
> > Is it possible to store conses with the car and cdr declared as double-floats
> > in order to avoid boxing; I tried few things without success.
> >
> > Kind regards
> > Taoufik
>
> Isn't it common practice (at least outside the Lisp community) to implement trees (or other graphs) as arrays? Then you could have two arrays, one with the data (as double-floats) one with (fixnum) indices.

I thought about this; I need to efficiently manage the indices (reusing indices of data that were destroyed);

-Taoufik

Marco Antoniotti

1/14/2016 8:30:00 AM

0

On Thursday, January 14, 2016 at 1:51:39 AM UTC+1, Günther Thomsen wrote:
> On Wednesday, January 13, 2016 at 12:49:08 PM UTC-8, Taoufik Dachraoui wrote:
> > Hi
> >
> > (defun (i j v)
> > (let ((a (make-array (2000 2000))))
> > (incf (aref a i j) v)))
> >
> > is much slower than:
> >
> > (defun (i j v)
> > (declare (double-foat v))
> > (let ((a (make-array (2000 2000) :element-type 'double-float)))
> > (incf (aref a i j) v)))
> >
> > How the implementation is storing the elements of the array
> > is different with and without declaration (I think that the elements
> > in the second function are stored unboxed in the array.
> >
> > I would like to do the same in my application where
> > I use a tree of conses where the leafs are double-floats;
> >
> > Is it possible to store conses with the car and cdr declared as double-floats
> > in order to avoid boxing; I tried few things without success.
> >
> > Kind regards
> > Taoufik
>
> Isn't it common practice (at least outside the Lisp community) to implement trees (or other graphs) as arrays? Then you could have two arrays, one with the data (as double-floats) one with (fixnum) indices.

AFAIK, not so much for dynamic structures like trees. Using array based implementations makes sense only if you really care about searches and traversals and your updates are few and sparse in time.

Cheers
--
MA

Taoufik Dachraoui

1/14/2016 9:30:00 AM

0

On Thursday, January 14, 2016 at 12:54:26 AM UTC+1, Barry Margolin wrote:
> In article <51ed6bf8-09af-4c04-889f-83f4650bb662@googlegroups.com>,
> Taoufik Dachraoui <dachraoui.taoufik@gmail.com> wrote:
>
> > On Wednesday, January 13, 2016 at 10:12:14 PM UTC+1, Dimitri Fontaine wrote:
> > > Taoufik Dachraoui <dachraoui.taoufik@gmail.com> writes:
> > > > Is it possible to store conses with the car and cdr declared as
> > > > double-floats
> > > > in order to avoid boxing; I tried few things without success.
> > >
> > > Did you try to use a defstruct for the tree leaves (and nodes maybe
> > > too)? You could use :type for the structure slots.
> > >
> > > --
> > > dim
> >
> > I did the following test to compare conses and defstruct:
>
> The suggestion was to use the :TYPE option in DEFSTRUCT, so it might
> unbox the slots. You didn't do that.
>
> > (BAR 20)
> >
> >
> > took 4,508 milliseconds (4.508 seconds) to run.
> >
> >
> > 4,361 milliseconds (4.361 seconds, 96.74%) of which was spent in GC.
>
> > ? (time (jar 20)) ; test for conses
> >
> >
> > (JAR 20)
> >
> >
> > took 2,255 milliseconds (2.255 seconds) to run.
> >
> >
> > 2,149 milliseconds (2.149 seconds, 95.30%) of which was spent in GC.
>
> >
> > ? (time (bar-sum (bar 20 t))) ; ; test for defstruct
> >
> >
> > (BAR-SUM (BAR 20 T))
> >
> >
> > took 5,650 milliseconds (5.650 seconds) to run.
> >
> >
> > 5,309 milliseconds (5.309 seconds, 93.96%) of which was spent in GC.
>
> > ? (time (jar-sum (jar 20 t))) ; test for conses
> > (JAR-SUM (JAR 20 T))
> > took 2,797 milliseconds (2.797 seconds) to run.
> > 2,564 milliseconds (2.564 seconds, 91.67%) of which was spent in GC.
>
> So in all your tests, almost all the time was spent in GC, not boxing
> and unboxing.
>
> --
> Barry Margolin, barmar@alum.mit.edu
> Arlington, MA
> *** PLEASE post questions in newsgroups, not directly to me ***

For the jar test (cons):
? (+ (* 8 (1- (expt 2 21))) (* 16 (expt 2 20)))
33554424 ; bytes
The conses is (* 8 (1- (expt 2 21))) = 16777208 bytes
The double-float is (* 16 (expt 2 20)) = 16777216 bytes
Total= 33554424 bytes

For the bar test (defstruct)
? (+ (* 16 (1- (expt 2 21))) (* 16 (expt 2 20)))
50331632
The conses is (* 16 (1- (expt 2 21))) = 33554416 bytes
The double-float is (* 16 (expt 2 20)) = 16777216 bytes
Total=50331632

As you can see the conses with defstruct is twice the conses when using cons

I did the test with the leaves using defstruct and type, and it is much slower to create the data structure
and the time to sum up the data is also slower than with conses

kind regards
Taoufik

Dimitri Fontaine

1/14/2016 8:18:00 PM

0

Taoufik Dachraoui <dachraoui.taoufik@gmail.com> writes:
>> The suggestion was to use the :TYPE option in DEFSTRUCT, so it might
>> unbox the slots. You didn't do that.
>
> As you can see the conses with defstruct is twice the conses when using cons

The idea is more something like:

(defstruct (myleaf (:type list))
(left 0.0 :type double-float)
(right 0.0 :type double-float))

Regards,
--
dim