April 08, 2012
On 4/8/12 12:05 PM, Walter Bright wrote:
> On 4/8/2012 7:53 AM, Andrei Alexandrescu wrote:
>> Once anyone asks for .ptr a conservative copy will be made.
>
> That could get expensive. You cannot just point into the small string
> part, because that may only exist temporarily on the stack. There are
> some pathological cases for this.

As I mentioned, the first call to .ptr changes representation, thus making the allocation that the optimization had saved. Things are not worse off than before.

Andrei

April 08, 2012
On Sun, Apr 08, 2012 at 10:00:37AM -0500, Andrei Alexandrescu wrote:
> On 4/8/12 8:54 AM, H. S. Teoh wrote:
> >Sorry, I've been busy at work and haven't had too much free time to work on it.  The current code is available on github:
> >
> >	https://github.com/quickfur/New-AA-implementation
> 
> Thanks for the update! Let me reiterate this is important work for many reasons, so it would be great if you got around to pushing it through.

I'll try my best to work on it when I have free time.


> Do you think someone else in the community could help you?
[...]

Well, I put the code up on github for a reason. :-) I'll only be too glad to have someone else contribute. The code itself isn't all that complicated; it's basically just a facsimile of aaA.d with many bugs fixed due to the fact that we now have direct access to key/value types.


T

-- 
"Life is all a great joke, but only the brave ever get the point." -- Kenneth Rexroth
April 08, 2012
On 2012-04-08 17:14:37 +0000, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> On 4/8/12 12:05 PM, Walter Bright wrote:
>> On 4/8/2012 7:53 AM, Andrei Alexandrescu wrote:
>>> Once anyone asks for .ptr a conservative copy will be made.
>> 
>> That could get expensive. You cannot just point into the small string
>> part, because that may only exist temporarily on the stack. There are
>> some pathological cases for this.
> 
> As I mentioned, the first call to .ptr changes representation, thus making the allocation that the optimization had saved. Things are not worse off than before.

This only works if the code has write access to that variable.

Also, if the variable is a temporary copy such as a by-value function parameter, only the representation of that temporary copy will be affected. Every time you call a function which calls .ptr a new copy will be allocated on the heap. If such a function call is part of a loop, it'll degrade performance noticeably.


-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

April 08, 2012
On 4/8/12 1:16 PM, Michel Fortin wrote:
> On 2012-04-08 17:14:37 +0000, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> said:
>> As I mentioned, the first call to .ptr changes representation, thus
>> making the allocation that the optimization had saved. Things are not
>> worse off than before.
>
> This only works if the code has write access to that variable.
>
> Also, if the variable is a temporary copy such as a by-value function
> parameter, only the representation of that temporary copy will be
> affected. Every time you call a function which calls .ptr a new copy
> will be allocated on the heap. If such a function call is part of a
> loop, it'll degrade performance noticeably.

I think there's a bit of a confusion. Could you please clarify with an example?

Thanks,

Andrei

April 08, 2012
On 4/8/12 1:26 PM, Andrei Alexandrescu wrote:
> On 4/8/12 1:16 PM, Michel Fortin wrote:
>> On 2012-04-08 17:14:37 +0000, Andrei Alexandrescu
>> <SeeWebsiteForEmail@erdani.org> said:
>>> As I mentioned, the first call to .ptr changes representation, thus
>>> making the allocation that the optimization had saved. Things are not
>>> worse off than before.
>>
>> This only works if the code has write access to that variable.
>>
>> Also, if the variable is a temporary copy such as a by-value function
>> parameter, only the representation of that temporary copy will be
>> affected. Every time you call a function which calls .ptr a new copy
>> will be allocated on the heap. If such a function call is part of a
>> loop, it'll degrade performance noticeably.
>
> I think there's a bit of a confusion. Could you please clarify with an
> example?

I think I understand:

string s;
foreach (lots)
  fun(s);

void fun(string s)
{
    ... s.ptr ...
}

I agree in this case there would be one allocation per call.


Andrei
April 08, 2012
On 2012-04-08 18:30:49 +0000, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> On 4/8/12 1:26 PM, Andrei Alexandrescu wrote:
>> On 4/8/12 1:16 PM, Michel Fortin wrote:
>>> Also, if the variable is a temporary copy such as a by-value function
>>> parameter, only the representation of that temporary copy will be
>>> affected. Every time you call a function which calls .ptr a new copy
>>> will be allocated on the heap. If such a function call is part of a
>>> loop, it'll degrade performance noticeably.
>> 
>> I think there's a bit of a confusion. Could you please clarify with an
>> example?
> 
> I think I understand:
> 
> string s;
> foreach (lots)
>    fun(s);
> 
> void fun(string s)
> {
>      ... s.ptr ...
> }

Exactly.

> I agree in this case there would be one allocation per call.

Basically, it's the same problem as receiving a const(char)[] as a function parameter and .idup-ing it in the function. The recommendation is for such functions to accept only immutable(char)[] and let the caller find a way to send an immutable string. That way, if the caller is repeatedly using the same string, it can reuse the efficient representation.

The same principles apply here: if your function needs to use .ptr, it should request a type on which you can call .ptr efficiently and let the caller find a way to pass think kind of string. To me, your small buffer optimization proposal looks like an argument in favor of creating a separate string type free from the constrains of offering at all time bare access to the bytes. If only that type could be the default...


-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

April 08, 2012
Le 08/04/2012 17:48, Michel Fortin a Ă©crit :
> On 2012-04-08 15:06:13 +0000, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
> 
>> On 4/8/12 9:59 AM, Michel Fortin wrote:
>>> But as soon as you take a pointer to that string, you break the immutability guaranty:
>>>
>>> immutable(char)[] s = "abcd";
>>> immutable(char)* p = s.ptr;
>>> s = "defg"; // assigns to where?
>>
>> Taking .ptr will engender a copy. A small regression will be that address of individual chars cannot be taken.
> 
> You know, many people have been wary of hidden memory allocations in the past. That's not going to make them happy. I'm not complaining, but I think .ptr should return null in those cases. Let people use toStringz when they need a C string, and let people deal with the ugly details themselves if they're using .ptr to bypass array bound checking. Because if someone used .ptr somewhere to bypass bound checking and instead he gets a memory allocation at each loop iteration… it won't be pretty.
> 
> And what about implicit conversions to const(char)[]? That too will
> require a copy, because otherwise it could point to the local stack
> frame where your immutable(char)[] resides. That said, maybe copies of
> small-string optimized immutable(char)[] could be small-string optimized
> const(char)[]. That'd not have any side effect since no one can have a
> mutable pointer/slice to the const copy anyway.
> 

You've raised some very good points. I also think this won't do.
It is quite clear with your examples (especially the temporary copy as a
by value function parameter) that the small strings have some very
specific semantics that are completely different from normal string
semantics. Therefore I also have the feeling that they should be another
type, sstring or something.

sstrings could be promoted to full string when necessary, but the whole thing would probably look very very ugly pretty soon. This to me smells like the idea of small string optimization should be put to rest for a while, requiring a little more thought.
April 09, 2012
On 08.04.2012 07:56, Andrei Alexandrescu wrote:

> For this to happen, we need to start an effort of migrating built-in
> arrays into runtime, essentially making them templates that the compiler
> lowers to. So I have two questions:

vote -= real.infinity.

That would kill D.

April 09, 2012
On 04/09/2012 10:24 AM, Don wrote:
> On 08.04.2012 07:56, Andrei Alexandrescu wrote:
>
>> For this to happen, we need to start an effort of migrating built-in
>> arrays into runtime, essentially making them templates that the compiler
>> lowers to. So I have two questions:
>
> vote -= real.infinity.
>
> That would kill D.
>

Why does this even compile?

void main(){
    long vote;
    vote -= real.infinity;
    assert(vote == long.min);
}
April 09, 2012
On 9 April 2012 11:24, Don <nospam@nospam.com> wrote:

> On 08.04.2012 07:56, Andrei Alexandrescu wrote:
>
>  For this to happen, we need to start an effort of migrating built-in
>> arrays into runtime, essentially making them templates that the compiler lowers to. So I have two questions:
>>
>
> vote -= real.infinity.
>
> That would kill D.


How do you figure?

After thinking on it a bit, I'm becoming a little worried about this move
for 2 rarely considered reasons:
Using lowering to a template, debug(/unoptimised) performance will probably
get a lot slower, which is really annoying. And debugging/stepping might
become considerably more annoying too, if every time I press F11 (step in)
over a function call that happens to receive an arg from an array, the
debugger then steps into the array templates index operator... We'd be no
better off than with STL, unless the language has clever ways of hiding
this magic from the debugger too, and optimising/inlining the index even in
debug builds...? But this is the built-in array, and not a library we can
optionally not use.