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
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
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 ***
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 ***
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.
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
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
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
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 ***
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
>>>>> "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
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 ***
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