Hm, ok. That's an interesting view on this. You're basically suggesting to 
introduce what is called in some languages a "value" type (records/structs that 
can only be used by value, not by reference). That's more or less what structs 
in C/C++ are when passed to and returned from functions as values.

I guess that should be possible in principle. But I'm wondering if this would 
go too far in the direction of requiring support from the language. What I had 
in mind was more some form of optimization declaration, that might as well be 
safely ignored by the compiler (or disabled in debug mode)…

Pascal

On 7 May 2012, at 14:28, Peter Seibel wrote:

> 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 <p...@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 <p...@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@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/

--
Pascal Costanza



_______________________________________________
pro mailing list
pro@common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro

Reply via email to