From: Martin Raspaud
Subject: type declarations
Date: 
Message-ID: <c9htdj$snk$1@news.u-bordeaux.fr>
Hi all,

I wrote a fonction like this one :

(defun convert-array (orig-array dest-array bits-per-sample)
   "Convert an array of (signed-byte bits-per-sample) in an array of 
complex numbers"
   (declare (optimize (speed 3)
		     (safety 0))
	   ;; (type (simple-array (signed-byte "bits-per-sample") (*)) orig-array)
	   (type (simple-array (complex double-float) *) dest-array)
	   (type fixnum bits-per-sample))
   (let ((divisor (calculation-returning-a-complex-number)))
     (declare (type (complex double-float) divisor))
     (loop for i from 0 below (length orig-array) do
	  (setf (aref dest-array i) (* (aref orig-array i) divisor)))))

As you can see, I want to optimize speed, and thus I am declaring 
(almost) everything.
The line I commented is my problem : I would like to declare orig-array 
as made of signed-byte of size "bits-per-sample". Since the type as to 
be known at compile-time I guess, this doesn't work.


So I thought that making a closure over such a function could be the 
solution :

(defun make-convert-array (bits-per-sample)
   #'(lambda (orig-array dest-array)
       (declare (optimize (speed 3)
			 (safety 0))
	       (type (simple-array (signed-byte bits-per-sample) (*)) orig-array)
	       (type (simple-array (complex double-float) *) dest-array)
	       (type fixnum bits-per-sample))
       (let ((divisor (calculation-returning-a-complex-number)))
	(declare (type (complex double-float) divisor))
	(loop for i from 0 below (length orig-array) do
	      (setf (aref dest-array i) (* (aref orig-array i) divisor))))))

However, (signed-byte bits-per-sample) is not recognized as a type. I 
understand that bits-per-sample has to be evaluated, but how ?

Any idea ?

Any comments about my way of thinking ?


Thanks

Martin

From: Paul F. Dietz
Subject: Re: type declarations
Date: 
Message-ID: <dpSdnSjhxd5rHSHdRVn-hQ@dls.net>
Martin Raspaud wrote:

> So I thought that making a closure over such a function could be the 
> solution :
> 
> (defun make-convert-array (bits-per-sample)
>   #'(lambda (orig-array dest-array)
>       (declare (optimize (speed 3)
>              (safety 0))
>            (type (simple-array (signed-byte bits-per-sample) (*)) 
> orig-array)
>            (type (simple-array (complex double-float) *) dest-array)
>            (type fixnum bits-per-sample))
>       (let ((divisor (calculation-returning-a-complex-number)))
>     (declare (type (complex double-float) divisor))
>     (loop for i from 0 below (length orig-array) do
>           (setf (aref dest-array i) (* (aref orig-array i) divisor))))))
> 
> However, (signed-byte bits-per-sample) is not recognized as a type. I 
> understand that bits-per-sample has to be evaluated, but how ?
> 
> Any idea ?
> 
> Any comments about my way of thinking ?

These can't work, since bits-per-sample has to be known at compile time.
The body of the lambda there is compiled at compile time, not when MAKE-CONVERT-ARRAY
is called.

You could defer compilation.  This would be something like this:

(defun make-convert-array (bits-per-sample)
   (compile
     nil
     `(lambda (orig-array dest-array)
         (declare (optimize (speed 3) (safety 0))
            (type (simple-array (signed-byte ,bits-per-sample) (*)) orig-array)
            (type (simple-array (complex double-float) *) dest-array))
       (let ((divisor (calculation-returning-a-complex-number)))
         (declare (type (complex double-float) divisor))
         (loop for i from 0 below (length orig-array) do
              (setf (aref dest-array i) (* (aref orig-array i) divisor))))))))

(note the backquote and comma)

You'd probably want to memoize (cache) this, since compiling can be
a heavyweight operation.

In situations where a special variable is known at read time, you can
use the #. reader macro to insert its value into a form to be compiled.

	Paul
From: Barry Margolin
Subject: Re: type declarations
Date: 
Message-ID: <barmar-91BC82.09311001062004@comcast.dca.giganews.com>
In article <······················@dls.net>,
 "Paul F. Dietz" <·····@dls.net> wrote:

> You'd probably want to memoize (cache) this, since compiling can be
> a heavyweight operation.

And if there are only a limited set of possible bits-per-sample 
possibilities, you could just write all of them (probably using a macro 
that's similar to the backquoted lambda expression we both provided) and 
put them in an array.  Then you can use

(funcall (aref *converters* bits-per-sample) ...)

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Barry Margolin
Subject: Re: type declarations
Date: 
Message-ID: <barmar-BEAEBD.09212501062004@comcast.dca.giganews.com>
In article <············@news.u-bordeaux.fr>,
 Martin Raspaud <········@labri.fr> wrote:
> So I thought that making a closure over such a function could be the 
> solution :
> 
> (defun make-convert-array (bits-per-sample)
>    #'(lambda (orig-array dest-array)
>        (declare (optimize (speed 3)
> 			 (safety 0))
> 	       (type (simple-array (signed-byte bits-per-sample) (*)) orig-array)
> 	       (type (simple-array (complex double-float) *) dest-array)
> 	       (type fixnum bits-per-sample))
>        (let ((divisor (calculation-returning-a-complex-number)))
> 	(declare (type (complex double-float) divisor))
> 	(loop for i from 0 below (length orig-array) do
> 	      (setf (aref dest-array i) (* (aref orig-array i) divisor))))))
> 
> However, (signed-byte bits-per-sample) is not recognized as a type. I 
> understand that bits-per-sample has to be evaluated, but how ?
> 
> Any idea ?

You have to use backquote to get the literal bit size into the type 
declaration, and then compile this to get the optimization.

(defun make-convert-array (bits-per-sample)
  (compile
    nil 
    `(lambda (orig-array dest-array)
       (declare (optimize (speed 3)
                          (safety 0))
                (type (simple-array (signed-byte ,bits-per-sample) (*))
                      orig-array)
                (type (simple-array (complex double-float) *)
                      dest-array)
                (type fixnum bits-per-sample))
       (let ((divisor (calculation-returning-a-complex-number)))
         (declare (type (complex double-float) divisor))
         (loop for i from 0 below (length orig-array) do
               (setf (aref dest-array i)
                     (* (aref orig-array i) divisor)))))))

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Joe Marshall
Subject: Re: type declarations
Date: 
Message-ID: <hdtvbdix.fsf@ccs.neu.edu>
Martin Raspaud <········@labri.fr> writes:

> Hi all,
>
> I wrote a fonction like this one :
>
> (defun convert-array (orig-array dest-array bits-per-sample)
>    "Convert an array of (signed-byte bits-per-sample) in an array of
> complex numbers"
>    (declare (optimize (speed 3)
> 		     (safety 0))
> 	   ;; (type (simple-array (signed-byte "bits-per-sample") (*)) orig-array)
> 	   (type (simple-array (complex double-float) *) dest-array)
> 	   (type fixnum bits-per-sample))
>    (let ((divisor (calculation-returning-a-complex-number)))
>      (declare (type (complex double-float) divisor))
>      (loop for i from 0 below (length orig-array) do
> 	  (setf (aref dest-array i) (* (aref orig-array i) divisor)))))
>
> As you can see, I want to optimize speed, and thus I am declaring
> (almost) everything.
> The line I commented is my problem : I would like to declare
> orig-array as made of signed-byte of size "bits-per-sample". Since the
> type as to be known at compile-time I guess, this doesn't work.
>
> Any idea ?

It is likely that you only have a few values of `bits-per-sample' that
are of interest, so replicating the code for each one (use a macro) is
probably a good bet.

Note that the array will be `upgraded' anyway so you'd really only
have to replicate the code a small number of times. 
From: Martin Raspaud
Subject: Re: type declarations
Date: 
Message-ID: <c9i9s5$1o9$1@news.u-bordeaux.fr>
Joe Marshall wrote:
> Martin Raspaud <········@labri.fr> writes:
> 
>>
>>Any idea ?
> 
> 
> It is likely that you only have a few values of `bits-per-sample' that
> are of interest, so replicating the code for each one (use a macro) is
> probably a good bet.
> 
> Note that the array will be `upgraded' anyway so you'd really only
> have to replicate the code a small number of times. 

What do you mean by 'upgraded' ?

Thanks for the idea anyway. I'm still not very at ease with macros, but 
I'll do this one, considering it seems to be a good solution.

Martin
From: Paul Dietz
Subject: Re: type declarations
Date: 
Message-ID: <40BCB7A0.3D4A48F1@motorola.com>
Martin Raspaud wrote:

> > Note that the array will be `upgraded' anyway so you'd really only
> > have to replicate the code a small number of times.
> 
> What do you mean by 'upgraded' ?

See the CLHS entry for the function UPGRADED-ARRAY-ELEMENT-TYPE.

	Paul
From: Joe Marshall
Subject: Re: type declarations
Date: 
Message-ID: <vfib9oo5.fsf@ccs.neu.edu>
Martin Raspaud <········@labri.fr> writes:

> Joe Marshall wrote:
>> Martin Raspaud <········@labri.fr> writes:
>>
>>>
>>>Any idea ?
>> It is likely that you only have a few values of `bits-per-sample'
>> that
>> are of interest, so replicating the code for each one (use a macro) is
>> probably a good bet.
>> Note that the array will be `upgraded' anyway so you'd really only
>> have to replicate the code a small number of times.
>
> What do you mean by 'upgraded' ?

Lisp systems are not required to implement every possible way to pack
bits, so if you ask for an array of a special type the system is
allowed to provide a more general type that can hold the type you
requested.  For instance, if you asked for an array of
(unsigned-byte 3), the system would be allowed to give you an array
with 4 bits per element, 8 bits per element, or even a general array
if that's all it knows about. 

See the Hyperspec section 15.1.2.1 Array Upgrading
From: Barry Margolin
Subject: Re: type declarations
Date: 
Message-ID: <barmar-A7EC97.13383901062004@comcast.dca.giganews.com>
In article <············@news.u-bordeaux.fr>,
 Martin Raspaud <········@labri.fr> wrote:

> Joe Marshall wrote:
> > Martin Raspaud <········@labri.fr> writes:
> > 
> >>
> >>Any idea ?
> > 
> > 
> > It is likely that you only have a few values of `bits-per-sample' that
> > are of interest, so replicating the code for each one (use a macro) is
> > probably a good bet.
> > 
> > Note that the array will be `upgraded' anyway so you'd really only
> > have to replicate the code a small number of times. 
> 
> What do you mean by 'upgraded' ?

Most implementations have discrete sets of specialized array types.  For 
instance, an implementation might have 8-bit, 16-bit, and 32-bit integer 
arrays.  If you specify (unsigned-byte 6) in your MAKE-ARRAY call, 
you'll actually get (unsigned-byte 8).  This is called upgrading -- see 
the function UPGRADED-ARRAY-ELEMENT-TYPE.

So there's not really much point in having a separate function for every 
type from (unsigned-byte 1) through (unsigned-byte 8) -- one function 
for all of them should suffice and be just as efficient.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Paul Dietz
Subject: Re: type declarations
Date: 
Message-ID: <40BCC8F1.6C48802F@motorola.com>
Barry Margolin wrote:

> So there's not really much point in having a separate function for every
> type from (unsigned-byte 1) through (unsigned-byte 8) -- one function
> for all of them should suffice and be just as efficient.

You'd better have a separate one for (unsigned-byte 1). :)

	Paul
From: Raymond Toy
Subject: Re: type declarations
Date: 
Message-ID: <sxdd64j9mfe.fsf@edgedsp4.rtp.ericsson.se>
>>>>> "Barry" == Barry Margolin <······@alum.mit.edu> writes:

    Barry> So there's not really much point in having a separate function for every 
    Barry> type from (unsigned-byte 1) through (unsigned-byte 8) -- one function 
    Barry> for all of them should suffice and be just as efficient.

Except for those implementations that also have (unsigned-byte 2) and
(unsigned-byte 4), like cmucl and sbcl.

Ray
From: Barry Margolin
Subject: Re: type declarations
Date: 
Message-ID: <barmar-0E7272.00541902062004@comcast.dca.giganews.com>
In article <···············@edgedsp4.rtp.ericsson.se>,
 Raymond Toy <···@rtp.ericsson.se> wrote:

> >>>>> "Barry" == Barry Margolin <······@alum.mit.edu> writes:
> 
>     Barry> So there's not really much point in having a separate function for 
>     every 
>     Barry> type from (unsigned-byte 1) through (unsigned-byte 8) -- one 
>     function 
>     Barry> for all of them should suffice and be just as efficient.
> 
> Except for those implementations that also have (unsigned-byte 2) and
> (unsigned-byte 4), like cmucl and sbcl.

I specifically said I was talking about a hypothetical implementation 
that only has 8, 16, and 32 (it was in the paragraph preceding the one 
you quoted).  Of course, on a different implementation you would need to 
have a different set of functions.  I think it would be possible to 
write a compile-time loop that figured out which functions are needed.

Someone else correctly pointed out that every implementation is required 
to support (unsigned-byte 1), which I forgot about.  But that doesn't 
change my point -- you only need distinct functions for the distinct 
array types.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Rahul Jain
Subject: Re: type declarations
Date: 
Message-ID: <874qpuo2f2.fsf@nyct.net>
Barry Margolin <······@alum.mit.edu> writes:

> Someone else correctly pointed out that every implementation is required 
> to support (unsigned-byte 1), which I forgot about.  But that doesn't 
> change my point -- you only need distinct functions for the distinct 
> array types.

All that's needed is a compiler-macro that gets the upgraded array
element type and generates a function for that type. Then save that
function away, indexed by the type so that later expansions of the
compiler macro can reuse the same function.

(defparameter *expansions* (make-hash-table :test 'equal))
(define-compiler-macro blah (... type)
  (let ((type (upgraded-array-element-type type)))
    (or (gethash *expansions* type)
        (setf (gethash *expansions* type)
              (compile nil `(lambda (...)
                               (declare (type ... ,type))
                               ...))))))

Not sure all the syntax is right, since I did it from memory, but
there's the general idea.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist