October 30, 2014
On Thursday, 30 October 2014 at 16:03:01 UTC, Steven Schveighoffer wrote:
> Keep in mind, sink delegate is not a singly implemented function, it's implemented wherever the output is done. So it's a lot of boilerplate to copy around.

Only 2 lines.

> This way, when write finds it runs out of space, first thing it does is sink the buff, then starts sinking the rest. In fact, you can just keep using buff once you sink it, to avoid future "extra calls" to sink.

I tried to make the code minimalistic. Thought that can give advantages.
October 30, 2014
>
> Before we start ripping apart our existing APIs, can we show that the performance is really going to be so bad? I know virtual calls have a bad reputation, but I hate to make these choices absent real data.
>
> For instance, D's underlying i/o system uses FILE *, which is about as virtual as you can get. So are you avoiding a virtual call to use a buffer to then pass to a virtual call later?
>
I think its debatable how useful this information would be but I've written a small D Program to try to explore the different performance statistics for various methods.  I've uploaded the code to my server, feel free to download/modify/use.

Here's the various methods I've tested.
/**
   Method 1: ReturnString
             string toString();
   Method 2: SinkDelegate
             void toString(void delegate(const(char)[]) sink);
   Method 3: SinkDelegateWithStaticHelperBuffer
             struct SinkStatic { char[64] buffer; void delegate(const(char)[]) sink; }
	     void toString(ref SinkStatic sink);
   Method 4: SinkDelegateWithDynamicHelperBuffer
             struct SinkDynamic { char[] buffer; void delegate(const(char)[]) sink; }
	     void toString(ref SinkDynamic sink);
	     void toString(SinkDynamic sink);

 */

Dmd/No Optimization (dmd dtostring.d):

RuntimeString run 1 (loopcount 10000000)
  Method 1     : 76 ms
  Method 2     : 153 ms
  Method 3     : 156 ms
  Method 4ref  : 157 ms
  Method 4noref: 167 ms
StringWithPrefix run 1 (loopcount 1000000)
  Method 1     : 159 ms
  Method 2     : 22 ms
  Method 3     : 80 ms
  Method 4ref  : 80 ms
  Method 4noref: 83 ms
ArrayOfStrings run 1 (loopcount 1000000)
  Method 1     : 1 sec and 266 ms
  Method 2     : 79 ms
  Method 3     : 217 ms
  Method 4ref  : 226 ms
  Method 4noref: 222 ms

Dmd/With Optimization (dmd -O dtostring.d):
RuntimeString run 1 (loopcount 10000000)
  Method 1     : 35 ms
  Method 2     : 67 ms
  Method 3     : 67 ms
  Method 4ref  : 72 ms
  Method 4noref: 70 ms
StringWithPrefix run 1 (loopcount 1000000)
  Method 1     : 154 ms
  Method 2     : 9 ms
  Method 3     : 86 ms
  Method 4ref  : 63 ms
  Method 4noref: 65 ms
ArrayOfStrings run 1 (loopcount 1000000)
  Method 1     : 1 sec and 252 ms
  Method 2     : 37 ms
  Method 3     : 191 ms
  Method 4ref  : 193 ms
  Method 4noref: 201 ms


I would like to make a note that passing around a stack allocated buffer to the various toString methods along with a sink delegate may not get much performance benefit.  One reason is because the logic can get a little hairy when trying to decide if the buffer is large enough for the string (see my code on ArrayOfStrings) which creates more code which can slow down the processor simply because there is more "code" memory the processor has to manage.  Also note that adding a helper method to the buffer/sink delegate doesn't help at all since this is equivalent to passing around the delegate (meaning you could just create the Buffered sink that calls the real delegate when it gets full and pass around it's own sink delegate).
October 30, 2014
On Thursday, 30 October 2014 at 18:54:00 UTC, Jonathan Marler
wrote:
>>
>> Before we start ripping apart our existing APIs, can we show that the performance is really going to be so bad? I know virtual calls have a bad reputation, but I hate to make these choices absent real data.
>>
>> For instance, D's underlying i/o system uses FILE *, which is about as virtual as you can get. So are you avoiding a virtual call to use a buffer to then pass to a virtual call later?
>>
> I think its debatable how useful this information would be but I've written a small D Program to try to explore the different performance statistics for various methods.  I've uploaded the code to my server, feel free to download/modify/use.
>
Woops heres the link:
http://marler.info/dtostring.d
October 30, 2014
On 10/30/14 2:53 PM, Jonathan Marler wrote:
>>
>> Before we start ripping apart our existing APIs, can we show that the
>> performance is really going to be so bad? I know virtual calls have a
>> bad reputation, but I hate to make these choices absent real data.
>>
>> For instance, D's underlying i/o system uses FILE *, which is about as
>> virtual as you can get. So are you avoiding a virtual call to use a
>> buffer to then pass to a virtual call later?
>>
> I think its debatable how useful this information would be but I've
> written a small D Program to try to explore the different performance
> statistics for various methods.  I've uploaded the code to my server,
> feel free to download/modify/use.
>
> Here's the various methods I've tested.
> /**
>     Method 1: ReturnString
>               string toString();
>     Method 2: SinkDelegate
>               void toString(void delegate(const(char)[]) sink);
>     Method 3: SinkDelegateWithStaticHelperBuffer
>               struct SinkStatic { char[64] buffer; void
> delegate(const(char)[]) sink; }
>           void toString(ref SinkStatic sink);
>     Method 4: SinkDelegateWithDynamicHelperBuffer
>               struct SinkDynamic { char[] buffer; void
> delegate(const(char)[]) sink; }
>           void toString(ref SinkDynamic sink);
>           void toString(SinkDynamic sink);
>
>   */
>
> Dmd/No Optimization (dmd dtostring.d):
>
> RuntimeString run 1 (loopcount 10000000)
>    Method 1     : 76 ms
>    Method 2     : 153 ms

I think the above result is deceptive, and the test isn't very useful. The RuntimeString toString isn't a very interesting data point -- it's simply a single string. Not many cases are like that. Most types have multiple members, and it's the need to *construct* a string from that data which is usually the issue.

But I would caution, the whole point of my query was about data on the platforms of which Manu speaks. That is, platforms that have issues dealing with virtual calls. x86 doesn't seem to be one of them.

-Steve
October 30, 2014
On Thursday, 30 October 2014 at 20:15:36 UTC, Steven Schveighoffer wrote:
> On 10/30/14 2:53 PM, Jonathan Marler wrote:
>>>
>>> Before we start ripping apart our existing APIs, can we show that the
>>> performance is really going to be so bad? I know virtual calls have a
>>> bad reputation, but I hate to make these choices absent real data.
>>>
>>> For instance, D's underlying i/o system uses FILE *, which is about as
>>> virtual as you can get. So are you avoiding a virtual call to use a
>>> buffer to then pass to a virtual call later?
>>>
>> I think its debatable how useful this information would be but I've
>> written a small D Program to try to explore the different performance
>> statistics for various methods.  I've uploaded the code to my server,
>> feel free to download/modify/use.
>>
>> Here's the various methods I've tested.
>> /**
>>    Method 1: ReturnString
>>              string toString();
>>    Method 2: SinkDelegate
>>              void toString(void delegate(const(char)[]) sink);
>>    Method 3: SinkDelegateWithStaticHelperBuffer
>>              struct SinkStatic { char[64] buffer; void
>> delegate(const(char)[]) sink; }
>>          void toString(ref SinkStatic sink);
>>    Method 4: SinkDelegateWithDynamicHelperBuffer
>>              struct SinkDynamic { char[] buffer; void
>> delegate(const(char)[]) sink; }
>>          void toString(ref SinkDynamic sink);
>>          void toString(SinkDynamic sink);
>>
>>  */
>>
>> Dmd/No Optimization (dmd dtostring.d):
>>
>> RuntimeString run 1 (loopcount 10000000)
>>   Method 1     : 76 ms
>>   Method 2     : 153 ms
>
> I think the above result is deceptive, and the test isn't very useful. The RuntimeString toString isn't a very interesting data point -- it's simply a single string. Not many cases are like that. Most types have multiple members, and it's the need to *construct* a string from that data which is usually the issue.
>
> But I would caution, the whole point of my query was about data on the platforms of which Manu speaks. That is, platforms that have issues dealing with virtual calls. x86 doesn't seem to be one of them.
>
> -Steve
Like I said "I think its debatable how useful this information would be".  Yes you are correct the RuntimeString isn't very interesting, it's just establishes a base to compare what happens in the "simplest" possible case.  In the real world this would almost never happen.  I provided the code to encourage others to modify it and maybe post some more interesting cases then what I already provided.  I wanted to get some "real world" examples of how each API would change the code.  I would love to see someone improve on my implementation on ArrayOfStrings.  I wrote this code in a couple hours so I'm sure there alot of room for improvement.
October 31, 2014
On Thursday, 30 October 2014 at 20:15:36 UTC, Steven Schveighoffer wrote:
> I think the above result is deceptive, and the test isn't very useful. The RuntimeString toString isn't a very interesting data point -- it's simply a single string. Not many cases are like that. Most types have multiple members, and it's the need to *construct* a string from that data which is usually the issue.
>
> But I would caution, the whole point of my query was about data on the platforms of which Manu speaks. That is, platforms that have issues dealing with virtual calls. x86 doesn't seem to be one of them.

OTOH, ArrayOfStrings shows that allocating is worse by several orders of magnitudes. This will not change on any architecture. And the simple sink variant is still faster than the rest by almost an order of magnitude, this may also be unlikely to be much different on these architectures.
October 31, 2014
On Monday, 27 October 2014 at 07:42:30 UTC, Benjamin Thaut wrote:
> Any reasons why such a pull request would not get accepted?
>
I did that for the exception hierarchy and sure would accept that for TypeInfo. Not so sure about Object itself, as it would nail down the API.
October 31, 2014
On Tuesday, 28 October 2014 at 21:32:14 UTC, ketmar via Digitalmars-d wrote:
> On Tue, 28 Oct 2014 08:37:43 -0400
>> I think a few simple functions can suffice for druntime's purposes. We don't need a kitchen sink function (pun intended).
> ah, those famous last words... ;-) from my observations it's enough to
> implement '%[+-]width[.maxlen]s' and the same for '%x'. i also added
> codes to skip arguments and to print all what's left ('%*'). i'm sure
> that it can be done in <10KB, and it will be very handy to have.
> druntime doesn't do alot of printing and string conversions anyway. and
> phobos is already ridden with templates and CTFE.

https://github.com/D-Programming-Language/druntime/pull/662
October 31, 2014
On Tuesday, 28 October 2014 at 23:06:32 UTC, Manu via Digitalmars-d wrote:
> Video games consoles are very real, and very now.

What architecture/platform?
The indirect function call argument seems a bit displaced for toString.

>> This puts the burden on the caller to ensure enough space is allocated. Or
>> you have to reenter the function to finish up the output. Neither of these
>> seem like acceptable drawbacks.
>
> Well that's why I open for discussion. I'm sure there's room for
> creativity here.

Well the obvious alternative is a printf style return, that tells you how much space is needed.

> It doesn't seem that unreasonable to reenter the function to me
> actually, I'd prefer a second static call in the rare event that a
> buffer wasn't big enough,

Second virtual call ;).
October 31, 2014
On 10/31/14 4:40 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>" wrote:
> On Thursday, 30 October 2014 at 20:15:36 UTC, Steven Schveighoffer wrote:
>> I think the above result is deceptive, and the test isn't very useful.
>> The RuntimeString toString isn't a very interesting data point -- it's
>> simply a single string. Not many cases are like that. Most types have
>> multiple members, and it's the need to *construct* a string from that
>> data which is usually the issue.
>>
>> But I would caution, the whole point of my query was about data on the
>> platforms of which Manu speaks. That is, platforms that have issues
>> dealing with virtual calls. x86 doesn't seem to be one of them.
>
> OTOH, ArrayOfStrings shows that allocating is worse by several orders of
> magnitudes. This will not change on any architecture. And the simple
> sink variant is still faster than the rest by almost an order of
> magnitude, this may also be unlikely to be much different on these
> architectures.

I find it hard to believe that the delegate version is going to be slower than the memory version on any architecture also. But I must defer to Manu as the expert in those architectures. This is why I asked for a test. The presented data is useful, but not for the purpose of my query. I need to know if it performs bad on these platforms, not how it performs on x86. We should be willing to entertain other proposals for how toString should work, but not if it's proven that what we have will suffice.

It should be possible to perform such a test without D support.

In any case, I think there is still room for improvement inside the implementations of toString as has been mentioned.

-Steve