November 16, 2006
Stewart Gordon wrote:
> So the whole ForStatement creates a scope, and Initialize, Test, Increment and the statement body all share this scope?

Yes.

> This ought to be a little clearer.  The way it's written at the moment ("that variable's scope extends through the end of NoScopeNonEmptyStatement") looks like a contradiction.

Good point.
November 16, 2006
Chris Nicholson-Sauls wrote:
> Jarrett Billingsley wrote:
> 
>> "Reiner Pope" <reiner.pope@REMOVE.THIS.gmail.com> wrote in message news:ejg8qd$475$1@digitaldaemon.com...
>>
>>> Jarrett Billingsley wrote:
>>
>>
>>
>>> I'm not sure if this is what you want, but here's a cast-free 'call' function:
>>>
>>> ...code...
>>
>>
>>
>> Ah!  That's an alright tradeoff.  I didn't actually know that you could set the .ptr and .funcptr properties of delegates; I thought they were read-only!  Cool.
>>
> 
> I didn't know this either... and it opens some ideas up to me...  Like, perhaps one could do the following:
> 
> # class Foo {
> #   void bar () { ... }
> # }
> #
> # Foo[] list = ... ;
> # auto dg = &list[0].bar;
> # dg();
> # foreach (x; list[1 .. $]) {
> #   dg.ptr = x;
> #   dg();
> # }
> 
> Might actually be slightly cheaper than a normal call in some cases, particularly of Foo and/or bar() are final.  Will have to test it...
> 
> -- Chris Nicholson-Sauls

Well, I tested it.  And the results are... pretty neutral, really.  Which is a plus in its own right, because it does at least mean one can use this trick without worry.  Some exemplar output from the test program:

                 Repeats:      100
              Iterations:     1000
               List size:      500
            Virtual call:        0 sec /       20 msec /    21417 usec
    Manipulated delegate:        0 sec /       18 msec /    19367 usec

                 Repeats:      100
              Iterations:     5000
               List size:      500
            Virtual call:        0 sec /       86 msec /    87139 usec
    Manipulated delegate:        0 sec /       86 msec /    86640 usec

                 Repeats:      100
              Iterations:     5000
               List size:       25
            Virtual call:        0 sec /        7 msec /     8117 usec
    Manipulated delegate:        0 sec /        6 msec /     6901 usec

So, yes, there is occasionally some speedup from using the manipulated delegate, but its nothing to scream about.  And it appears that, as the size of the data increases, or the number of iterations over it, the times start to drift toward each other and even out. ("Repeats" in the output is the number of times the test was run, with the results being averaged out.)

-- Chris Nicholson-Sauls
November 17, 2006
Walter Bright wrote:
> Stewart Gordon wrote:
>> So the whole ForStatement creates a scope, and Initialize, Test, Increment and the statement body all share this scope?
> 
> Yes.
> 
>> This ought to be a little clearer.  The way it's written at the moment ("that variable's scope extends through the end of NoScopeNonEmptyStatement") looks like a contradiction.
> 
> Good point.

Moreover, this whole way of putting things can lead one to wonder whether variables declared in the body are reinitialized each iteration....

Stewart.

-- 
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/M d- s:-@ C++@ a->--- UB@ P+ L E@ W++@ N+++ o K-@ w++@ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++++ h-- r-- !y
------END GEEK CODE BLOCK------

My e-mail is valid but not my primary mailbox.  Please keep replies on the 'group where everyone may benefit.
November 17, 2006
Stewart Gordon wrote:
> Moreover, this whole way of putting things can lead one to wonder whether variables declared in the body are reinitialized each iteration....

Why? A declaration is just like an assignment statement. It is executed when it appears. If it appears in a loop, it is executed each iteration of that loop.
November 17, 2006
Chris Nicholson-Sauls wrote:
> Chris Nicholson-Sauls wrote:
> 
>> Jarrett Billingsley wrote:
>>
>>> "Reiner Pope" <reiner.pope@REMOVE.THIS.gmail.com> wrote in message news:ejg8qd$475$1@digitaldaemon.com...
>>>
>>>> Jarrett Billingsley wrote:
>>>
>>>
>>>
>>>
>>>> I'm not sure if this is what you want, but here's a cast-free 'call' function:
>>>>
>>>> ...code...
>>>
>>>
>>>
>>>
>>> Ah!  That's an alright tradeoff.  I didn't actually know that you could set the .ptr and .funcptr properties of delegates; I thought they were read-only!  Cool.
>>>
>>
>> I didn't know this either... and it opens some ideas up to me...  Like, perhaps one could do the following:
>>
>> # class Foo {
>> #   void bar () { ... }
>> # }
>> #
>> # Foo[] list = ... ;
>> # auto dg = &list[0].bar;
>> # dg();
>> # foreach (x; list[1 .. $]) {
>> #   dg.ptr = x;
>> #   dg();
>> # }
>>
>> Might actually be slightly cheaper than a normal call in some cases, particularly of Foo and/or bar() are final.  Will have to test it...
>>
>> -- Chris Nicholson-Sauls
> 
> 
> Well, I tested it.  And the results are... pretty neutral, really.  Which is a plus in its own right, because it does at least mean one can use this trick without worry.  Some exemplar output from the test program:
> 
>                  Repeats:      100
>               Iterations:     1000
>                List size:      500
>             Virtual call:        0 sec /       20 msec /    21417 usec
>     Manipulated delegate:        0 sec /       18 msec /    19367 usec
> 
>                  Repeats:      100
>               Iterations:     5000
>                List size:      500
>             Virtual call:        0 sec /       86 msec /    87139 usec
>     Manipulated delegate:        0 sec /       86 msec /    86640 usec
> 
>                  Repeats:      100
>               Iterations:     5000
>                List size:       25
>             Virtual call:        0 sec /        7 msec /     8117 usec
>     Manipulated delegate:        0 sec /        6 msec /     6901 usec
> 
> So, yes, there is occasionally some speedup from using the manipulated delegate, but its nothing to scream about.  And it appears that, as the size of the data increases, or the number of iterations over it, the times start to drift toward each other and even out. ("Repeats" in the output is the number of times the test was run, with the results being averaged out.)
> 
> -- Chris Nicholson-Sauls

Well... what do you know.  I get back home and take a look over the test again... and find that I actually had made an error.  The data wasn't getting reset between the two loops (forgot to call the reset() function... very duh moment)... which means the results for the delegate style were actually an average of its runs /plus/ the runs of the normal call!  So, I fixed it... and here are a couple of sample runs:

                 Repeats:      100
              Iterations:     1000
               List size:      500
            Virtual call:        0 sec /       21 msec /    21898 usec
    Manipulated delegate:        0 sec /       16 msec /    17367 usec

                 Repeats:      100
              Iterations:     5000
               List size:      500
            Virtual call:        0 sec /       97 msec /    98014 usec
    Manipulated delegate:        0 sec /       93 msec /    94004 usec

                 Repeats:      100
              Iterations:     5000
               List size:       25
            Virtual call:        0 sec /        8 msec /     9436 usec
    Manipulated delegate:        0 sec /        3 msec /     4211 usec

Definitely a more significant difference than I'd previously thought!  Still not a massive difference, and the results actually vary quite a bit between runs.  (Sometimes by several milliseconds.)  And still as the data set size or iterations grow, the times approach equality, sometimes within a couple hundred microseconds.  (Every so often, the normal calls when even perform a little faster.)

I suppose, if this could cleanly and optimally be generalized into a template, it /might/ provide some benefit to those wanting to eek out as much speed as possible.  Albeit with inconsistant benefits.

-- Chris Nicholson-Sauls
November 17, 2006
The Phobos page is missing a link to std.traits in the left column!
November 17, 2006
Walter Bright wrote:
> Stewart Gordon wrote:
>> Moreover, this whole way of putting things can lead one to wonder whether variables declared in the body are reinitialized each iteration....
> 
> Why? A declaration is just like an assignment statement. It is executed when it appears. If it appears in a loop, it is executed each iteration of that loop.

True in general, but....

- in general, a declaration such as

    int x;

doesn't look like an assignment statement

- the idea of the same variable being declared multiple times in the same instance of being in the same scope is alien to many

- there are also static local variables, which aren't reset each time


Moreover, your claim that the _body_ of a for loop doesn't create a scope would also mean that ScopeGuardStatements and scope (fka auto) object references within the body are not processed at the end of each iteration, but saved until the end of the whole for loop.  Absolutely not the behaviour I'm experiencing.

Stewart.

-- 
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/M d- s:-@ C++@ a->--- UB@ P+ L E@ W++@ N+++ o K-@ w++@ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++++ h-- r-- !y
------END GEEK CODE BLOCK------

My e-mail is valid but not my primary mailbox.  Please keep replies on the 'group where everyone may benefit.
November 17, 2006
Stewart Gordon wrote:
> Walter Bright wrote:
>> Stewart Gordon wrote:
>>> Moreover, this whole way of putting things can lead one to wonder whether variables declared in the body are reinitialized each iteration....
>>
>> Why? A declaration is just like an assignment statement. It is executed when it appears. If it appears in a loop, it is executed each iteration of that loop.
> 
> True in general, but....
> 
> - in general, a declaration such as
> 
>     int x;
> 
> doesn't look like an assignment statement

Since D has default assignment in declarations, it is one. It behaves the same way in C++ (for types that have default constructors). D isn't doing anything weird here.

> - the idea of the same variable being declared multiple times in the same instance of being in the same scope is alien to many

? You cannot declare the same variable multiple times in the same scope.

> - there are also static local variables, which aren't reset each time

Again, this behaves just like C++, and should not be surprising.

> Moreover, your claim that the _body_ of a for loop doesn't create a scope would also mean that ScopeGuardStatements and scope (fka auto) object references within the body are not processed at the end of each iteration, but saved until the end of the whole for loop.  Absolutely not the behaviour I'm experiencing.

The for statement itself generates a scope, not the { }. This behaves exactly like the for statement in C++.
November 17, 2006
Tomas Lindquist Olsen wrote:
> The Phobos page is missing a link to std.traits in the left column!

And one for std.typetuple as well.

-- Chris Nicholson-Sauls
November 17, 2006
Walter Bright wrote:
> Stewart Gordon wrote:
<snip>
>> - the idea of the same variable being declared multiple times in the same instance of being in the same scope is alien to many
> 
> ? You cannot declare the same variable multiple times in the same scope.

What I meant is that, in order for the count variable to continue across iterations, it would imply that the control enters and leaves the scope only once for the whole for loop.  And so

    for (int x = 0; x < 3; x++) {
        int y = x*x;
        writefln(y);
    }

would seem equivalent to

    {
        int x = 0;
        int y0 = x * x;
        writefln(y0);
        x++;
        int y1 = x * x;
        writefln(y1);
        x++;
        int y2 = x * x;
        writefln(y2);
        x++;
    }

i.e. in each instance a new y is declared, hiding the previous, but they don't go out of scope till the end of the loop.

OK, so there's also the goto, which could be used to go back to before a declaration statement and execute it again.  JTAI, I suppose that explains something.  You're not really redeclaring it, but merely resetting its value.  So while declarations may be anywhere within a function, at the internal level the placement of a declaration is merely the point at which the variable is given its initial value.

>> - there are also static local variables, which aren't reset each time
> 
> Again, this behaves just like C++, and should not be surprising.
> 
>> Moreover, your claim that the _body_ of a for loop doesn't create a scope would also mean that ScopeGuardStatements and scope (fka auto) object references within the body are not processed at the end of each iteration, but saved until the end of the whole for loop.  Absolutely not the behaviour I'm experiencing.
> 
> The for statement itself generates a scope, not the { }. This behaves exactly like the for statement in C++.

Does the flow of control enter and leave this scope only once for the whole for loop or once for each iteration?

- If once for the whole loop, how would you explain the behaviour of scope guards that I'm experiencing?
- If for each iteration, then how does the counter variable (if declared in the Initialize) keep hold of its value between iterations?

Stewart.

-- 
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/M d- s:-@ C++@ a->--- UB@ P+ L E@ W++@ N+++ o K-@ w++@ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++++ h-- r-- !y
------END GEEK CODE BLOCK------

My e-mail is valid but not my primary mailbox.  Please keep replies on the 'group where everyone may benefit.