[pro] return-with-dynamic-extent...

Peter Seibel peter at gigamonkeys.com
Mon May 7 12:28:05 UTC 2012


Yes, I'm aware that EQL is defined for all objects. That's what I'm getting
at. It's like EQ except in the case of numbers and chars because those are
immutable objects that can be copied willy-nilly under the covers. EQL is
an abstraction that hides those under the covers shenanigans making
equivalently valued numbers and chars "the same" object (in the terminology
of the spec). It seems to me that in your example since you only made one
object it should be "the same" from the point of view of EQL even if the
compiler has the latitude to make copies that render *x* and the return
value of (foo) not EQ. Or to put it another way, it seems to me that what
your proposing is a mechanism for users to define new types that have some
of the efficiency advantages that currently only numbers and chars are
allowed so I think they should be fit into the language in a similar way.

-Peter

On Sun, May 6, 2012 at 11:25 PM, Pascal Costanza <pc at p-cos.net> wrote:

> Hi,
>
> I have something like the following in mind:
>
> (defvar *x*)
>
> (defun foo ()
>    (let ((x (make-bar …)))
>       (declare (dynamic-extent x))
>       (setq *x* x)
>       (return-with-dynamic-extent x)))
>
> (defun test ()
>    (eql *x* (foo)))
>
> If the result of test is not guaranteed to be t, implementations should
> have more freedom to move stack-allocated values up and down the stack,
> which could potentially simplify things internally. For users, I don't
> think this would be a great restriction, because it seems to me it's
> unlikely that would want to rely on eq/eql in the first place here…
>
> [eql is not only defined on numbers and chars, but also other objects, and
> is the same as eq in most cases…]
>
> Pascal
>
> On 7 May 2012, at 01:41, Peter Seibel wrote:
>
> This is verging on worrying about the color of the bikeshed but why should
> EQL not necessarily work with these objects. I can see EQ not working, by
> analogy with numbers and characters but EQL should perhaps do a value
> comparison. (Hmmm, are you assuming that these objects are immutable? Sort
> of seems like they'd better be.)
>
> -Peter
>
> On Sun, May 6, 2012 at 12:27 PM, Pascal Costanza <pc at p-cos.net> wrote:
>
>> Hi,
>>
>> Here is a description of what I think is a feature missing in Common
>> Lisp, both the standard and current implementations, namely a more complete
>> way to ensure stack allocation of objects.
>>
>> Based on some recent experience with translating some C++ benchmarks to
>> Common Lisp - more specifically the fluid animate benchmark from the PARSEC
>> benchmark suite - being able to allocate objects on the stack can be a
>> major performance win. Unfortunately, this is harder than necessary to
>> achieve in Common Lisp.
>>
>> Here is a simplified example. Assume you have the following class
>> definition in C++:
>>
>> struct vec {
>>  int x; int y; int z;
>>  vec(int x, int y, int z): x(x), y(y), z(z) {}
>> };
>>
>> Then you can define the following operations on such a class:
>>
>> vec vplus(vec v1, vec v2) {
>>  vec v(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z);
>>  return vec;
>> }
>>
>> In such a function definition, the local variable v is stack allocated,
>> plus the return value is returned by value, not by reference. This holds
>> for any intermediate values as well, so if you have other operations like
>> vminus, vmul and vdiv, etc., defined accordingly, you can write expressions
>> like this:
>>
>> vplus(v1, vmul(v2, v3));
>>
>> …and rest assured that all intermediate values are allocated on the stack
>> as well. (C++ loses some performance in some cases because it can happen
>> that it relies too much on copying of data structures, but this has been
>> largely resolved in C++11, where move operations are supported really well.)
>>
>> In Common Lisp, you can declare local variables with dynamic extent:
>>
>> (let ((v (make-vec :x 1 :y 2 :z 3)))
>>  (declare (dynamic-extent v))
>>  …)
>>
>> This has the effect that (in Common Lisp that support this) the struct
>> that v refers to is stack allocated. Unfortunately, Common Lisp
>> implementations can only do this for data structure constructors that the
>> implementation knows about - there is no way for users to add additional
>> operations to the set of known dynamic extent 'allocators.'
>>
>> For example, if you say the following:
>>
>> (defun vplus (v1 v2)
>>  (let ((v (make-vec :x (+ (vec-x v1) (vec-x v2))
>>                     :y (+ (vec-y v1) (vec-y v2))
>>                     :z (+ (vec-z v1) (vec-z v2)))))
>>    (declare (dynamic-extent v))
>>    v))
>>
>> …you can be very sure that the resulting program will fail, because the
>> reference to the stack allocated data structure is returned, but the data
>> will be gone afterwards. This had the consequence that in our attempts at
>> translating the fluid animate benchmark, we had to introduce all
>> intermediate results explicitly in separate variables, and be very explicit
>> about all the necessary computational steps. This was very tedious and the
>> result is not nice to look at.
>>
>> What would be nice is, if we could instead have some form of saying that
>> we want to return a data structure on the stack:
>>
>> (defun vplus (v1 v2)
>>  (let ((v (make-vec :x (+ (vec-x v1) (vec-x v2))
>>                     :y (+ (vec-y v1) (vec-y v2))
>>                     :z (+ (vec-z v1) (vec-z v2)))))
>>    (declare (dynamic-extent v))
>>    (return-with-dynamic-extent v))
>>
>> The idea is that now, the data that v refers to is returned by value on
>> the stack. If the call site receives the value with a matching
>> dynamic-extent declaration, then v can just stay on the stack. Same
>> (hopefully) for intermediate results in more complex expressions.
>>
>> Just like dynamic-extent declarations, an implementation should be
>> allowed to ignore return-with-dynamic-extent and replace it with a mere
>> return form.
>>
>> To be able to support move operations on the stack, it may be interesting
>> to specify that eq and eql are not reliable anymore for data that is return
>> by return-with-dynamic-extent.
>>
>> I presented the idea at ELS'12 in Zadar, and Christophe Rhodes commented
>> that it may also be necessary to have some form of global declaim that
>> states that a function will always return something with
>> return-with-dynamic-extent, so it's easier for the compiler to know what to
>> do with a return value.
>>
>> I don't claim that I have completely thought this through, but I think
>> this should be doable and shouldn't have too many negative consequences for
>> the rest of a system that doesn't use dynamic extents.
>>
>> I won't be able to implement this, but I just wanted to make sure that
>> the idea is on the table, so someone who is interested may pick it up…
>>
>>
>> Pascal
>>
>> --
>> Pascal Costanza
>> The views expressed in this email are my own, and not those of my
>> employer.
>>
>>
>>
>>
>> _______________________________________________
>> pro mailing list
>> pro at common-lisp.net
>> http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro
>>
>
>
>
> --
> Peter Seibel
> http://www.gigamonkeys.com/
>
>
> --
> Pascal Costanza
>
>
>
>


-- 
Peter Seibel
http://www.gigamonkeys.com/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/pro/attachments/20120507/80441027/attachment.html>


More information about the pro mailing list