bearophile Wrote:
> The Cray Inc is designing the Chapel Language:
> http://chapel.cs.washington.edu/> The following notes are from the specification V. 0.750:
> http://chapel.cs.washington.edu/spec-0.750.pdf> I think it's rather cute, it looks like a cross between C++, Fortress and Python. Here are few things I think can be interesting for D designers too:
> > - Chap. 11.5 page 65, swap operator (useful but probably not enough to justify a new operator)
swap is very useful especially in exception safe programming.
I would like to see swap used as the default implementation of D's transfer constructor (and C++0x's forthcoming move constructor)

Bruce Adams wrote:
> bearophile Wrote:
> >> The Cray Inc is designing the Chapel Language:
>> http://chapel.cs.washington.edu/>> The following notes are from the specification V. 0.750:
>> http://chapel.cs.washington.edu/spec-0.750.pdf>> I think it's rather cute, it looks like a cross between C++, Fortress and Python. Here are few things I think can be interesting for D designers too:
>>>> - Chap. 11.5 page 65, swap operator (useful but probably not enough to justify a new operator)
> > swap is very useful especially in exception safe programming.
Certainly. But how much of a need is there for swap in a reference-based language like D? I would think that most swaps would simply operate on references. Unless you're talking about structs, and structs do not have copy ctors so there is little risk of an exception.
Sean

Derek Parnell Wrote:
> On Wed, 03 Oct 2007 14:53:57 -0400, Bruce Adams wrote:
> > > bearophile Wrote:
> > > >> The Cray Inc is designing the Chapel Language:
> >> http://chapel.cs.washington.edu/> >> The following notes are from the specification V. 0.750:
> >> http://chapel.cs.washington.edu/spec-0.750.pdf> >> I think it's rather cute, it looks like a cross between C++, Fortress and Python. Here are few things I think can be interesting for D designers too:
> >> > >> - Chap. 11.5 page 65, swap operator (useful but probably not enough to justify a new operator)
> > > > swap is very useful especially in exception safe programming.
> > I would like to see swap used as the default implementation of D's transfer constructor (and C++0x's forthcoming move constructor)
> > Isn't a simple template such this sufficient?
> > template swap(TYPE)
> {
> void swap(ref TYPE a, ref TYPE b)
> {
> synchronized
> {
> TYPE temp = a;
> a = b;
> b = temp;
> }
> }
> > }
> > > --
> Derek Parnell
> Melbourne, Australia
> skype: derek.j.parnell
Most of the time it is.
Sometimes swapping references is sufficient but sometimes you actually
want real data to change. As the type gets bigger you start
to prefer decomposing swap into swaps of individual members rather than 2 whole copes. Do you really want to copy 120Mb twice?
Actually you should decomposing the swap from the outset if possible
(second to swapping just references if possible obviously).
Swaps also have to be atomic and never throw exceptions.
A more advanced template might use tuples recursively to do this.
I would want that in the standard library.
Some hardware platforms may support swaps directly. In reversible computing (okay so no-one in the real world uses that - yet) it is a fundamental operation. Then it becomes a compiler decision how best to implement it. If it is used as the default implementation for transfer constructors this goes double.
I suspect a standard library implementation is sufficient from the outset
leaving it up to the compiler vendor to decide how clever he or she needs to be.
It continues to suprise me that we don't prefer swap to destructive assignment more generally. Perhaps it feels less natural after years of the C way?
Bruce.

On Wed, Oct 03, 2007 at 06:25:08PM -0700, Gregor Richards wrote:
> For the ridiculously-insane:
>> void swap(T)(ref T a, ref T b)
> {
> synchronized {
> // this should be some kind of static for ...
> for (size_t i = 0; i < (a.sizeof/size_t.sizeof); i++) {
> (cast(size_t*) &a)[i] ^= (cast(size_t*) &b)[i];
> (cast(size_t*) &b)[i] = (cast(size_t*) &a)[i] ^ (cast(size_t*) &b)[i];
> (cast(size_t*) &a)[i] ^= (cast(size_t*) &b)[i];
> }
> }
> }
>> Add some loop unrolling and that's more efficient than memcpy :P
And rather devestating if another thread causes a garbage collection.
Doesn't the synchronized just lock this section of code, not the whole
program?
But, why would it be any more efficient than the inside of the loop just
saying:
size_t tmp = ((cast(size_t*) &a)[i];
((cast(size_t*) &a)[i] = ((cast(size_t*) &b)[i];
((cast(size_t*) &b)[i] = tmp;
This does 2 reads and 2 writes. The xor version does 4 reads and 3 writes.
How could it be faster?
The xor trick really only helps you if you are one architecture where you
don't have a register to spare. Shuffling through a register is going to
be much faster than multiple reads and writes to memory.
David

David Brown Wrote:
> On Wed, Oct 03, 2007 at 06:25:08PM -0700, Gregor Richards wrote:
> > > For the ridiculously-insane:
> >> > void swap(T)(ref T a, ref T b)
> > {
> > synchronized {
> > // this should be some kind of static for ...
> > for (size_t i = 0; i < (a.sizeof/size_t.sizeof); i++) {
> > (cast(size_t*) &a)[i] ^= (cast(size_t*) &b)[i];
> > (cast(size_t*) &b)[i] = (cast(size_t*) &a)[i] ^ (cast(size_t*)
> > &b)[i];
> > (cast(size_t*) &a)[i] ^= (cast(size_t*) &b)[i];
> > }
> > }
> > }
> >> > Add some loop unrolling and that's more efficient than memcpy :P
> > And rather devestating if another thread causes a garbage collection. Doesn't the synchronized just lock this section of code, not the whole program?
> > But, why would it be any more efficient than the inside of the loop just saying:
> > size_t tmp = ((cast(size_t*) &a)[i];
> ((cast(size_t*) &a)[i] = ((cast(size_t*) &b)[i];
> ((cast(size_t*) &b)[i] = tmp;
> > This does 2 reads and 2 writes. The xor version does 4 reads and 3 writes. How could it be faster?
> > The xor trick really only helps you if you are one architecture where you don't have a register to spare. Shuffling through a register is going to be much faster than multiple reads and writes to memory.
> > David
I think this is very much implementation defined.
If you are operating on a block of memory rather than data small
enough to fit in a register you have to dereference memory anyway.
And lets not forget platforms with some sort of exchange registers as a primative operation.
Though, I dislike joining the army of people rushing to suggest adding more and more (often unnecessary) features to an already feature rich language, I think that it is implementation defined justifies it being a low level operator implemented by the compiler. Swap is primative enough to have an unambiguous meaning and unlikely to confuse noobs. I'd very much like to hear what a (the) compiler writer has to say on the subject (especially in light of transfer constructors). I wonder if Walter is reading?
Regards,
Bruce.

Bruce Adams wrote:
> David Brown Wrote:
> >> On Wed, Oct 03, 2007 at 06:25:08PM -0700, Gregor Richards wrote:
>>>>> For the ridiculously-insane:
>>>>>> void swap(T)(ref T a, ref T b)
>>> {
>>> synchronized {
>>> // this should be some kind of static for ...
>>> for (size_t i = 0; i < (a.sizeof/size_t.sizeof); i++) {
>>> (cast(size_t*) &a)[i] ^= (cast(size_t*) &b)[i];
>>> (cast(size_t*) &b)[i] = (cast(size_t*) &a)[i] ^ (cast(size_t*) &b)[i];
>>> (cast(size_t*) &a)[i] ^= (cast(size_t*) &b)[i];
>>> }
>>> }
>>> }
>>>>>> Add some loop unrolling and that's more efficient than memcpy :P
>> And rather devestating if another thread causes a garbage collection.
>> Doesn't the synchronized just lock this section of code, not the whole
>> program?
>>>> But, why would it be any more efficient than the inside of the loop just
>> saying:
>>>> size_t tmp = ((cast(size_t*) &a)[i];
>> ((cast(size_t*) &a)[i] = ((cast(size_t*) &b)[i];
>> ((cast(size_t*) &b)[i] = tmp;
>>>> This does 2 reads and 2 writes. The xor version does 4 reads and 3 writes.
>> How could it be faster?
>>>> The xor trick really only helps you if you are one architecture where you
>> don't have a register to spare. Shuffling through a register is going to
>> be much faster than multiple reads and writes to memory.
>>>> David
> > I think this is very much implementation defined.
> If you are operating on a block of memory rather than data small
> enough to fit in a register you have to dereference memory anyway.
> And lets not forget platforms with some sort of exchange registers as a primative operation.
> Though, I dislike joining the army of people rushing to suggest adding more and more (often unnecessary) features to an already feature rich language, I think that it is implementation defined justifies it being a low level operator implemented by the compiler. Swap is primative enough to have an unambiguous meaning and unlikely to confuse noobs. I'd very much like to hear what a (the) compiler writer has to say on the subject (especially in light of transfer constructors). I wonder if Walter is reading?
> > Regards,
> > Bruce.
I find it hard to believe that a compiler couldn't recognize that this is a swap operation:
tmp = a;
a = b;
b = tmp;
If it's not harder than I think for some reason, then it's not really needed in the language.
--bb