toString refactor in druntime

Steven Schveighoffer via Digitalmars-d digitalmars-d at puremagic.com
Mon Nov 3 18:49:55 PST 2014


On 11/3/14 6:05 PM, Jonathan Marler wrote:
> On Monday, 3 November 2014 at 22:33:25 UTC, Steven Schveighoffer wrote:
>> On 11/3/14 4:40 PM, Walter Bright wrote:
>>> On 11/3/2014 8:09 AM, Steven Schveighoffer wrote:
>>>> It is a huge difference to say EVERYONE who implements toString will
>>>> take any
>>>> templated type that purports to be an output range, vs giving one case
>>>> to handle.
>>>
>>> All an output range is is a type with a 'put' method. That's it. You're
>>> making it out to be far more complex than it is.
>>>
>>
>> Directly from the docs:
>> (http://dlang.org/phobos/std_range.html#isOutputRange)
>>
>> void myprint(in char[] s) { }
>> static assert(isOutputRange!(typeof(&myprint), char));
>>
>> No 'put' in sight, except as a substring of isOutputRange.
>>
>> I don't think you realize what a beast supporting all output ranges
>> is, or using them (hint: calling r.put for a generic output range is
>> an ERROR).
>>
>> -Steve
>
> In many cases templates are good because they provide the a way for the
> programmer to use a library optimized for their particular application.
> This is the case for the toString function.  An argument can be made
> that using templates is dangerous because if they are used incorrectly,
> the number of template instantiates can blow up.  But this can always be
> solved by the programmer by changing all their template calls to use the
> same template parameters.  This allows the template solution to
> simultaneously support a sink that represents a real function, or a
> delegate, or whatever the application needs.

If we make toString a template, we precludes it as a virtual function, 
and we force the object to expose its inner workings.

I think the template solution has advantages, one being the possibility 
for optimization. But I don't think the gains are significant enough. 
It's also more complex than necessary.

> I understand that people like having a binary library that instantiates
> it's own functions that have a static interface and I think there's
> value to that.  But most of the value is in dynamic libraries that the
> compiler cannot optimize.  When the compiler can optimize, let it:)
>
> I updated my test code to use a templated sink, here the link:
>
> http://marler.info/dtostring.d
>
>
>     Method 1: ReturnString
>               string toString();
>     Method 2: SinkDelegate
>               void toString(void delegate(const(char)[]) sink);
>     Method 3: SinkTemplate
>               void toString(T)(T sink) if(isOutputRange!(T,const(char)[]));
>     Method 4: SinkDelegateWithStaticHelperBuffer
>               struct SinkStatic { char[64] buffer; void
> delegate(const(char)[]) sink; }
>           void toString(ref SinkStatic sink);
>     Method 5: SinkDelegateWithDynamicHelperBuffer
>               struct SinkDynamic { char[] buffer; void
> delegate(const(char)[]) sink; }
>           void toString(ref SinkDynamic sink);
>           void toString(SinkDynamic sink);
>
>
> (DMD Compiler on x86) "dmd dtostring.d"
> RuntimeString run 1 (loopcount 10000000)
>    Method 1     : 76 ms
>    Method 2     : 153 ms
>    Method 3     : 146 ms
>    Method 4     : 157 ms
>    Method 5ref  : 165 ms
>    Method 5noref: 172 ms
> StringWithPrefix run 1 (loopcount 1000000)
>    Method 1     : 149 ms
>    Method 2     : 22 ms
>    Method 3     : 21 ms
>    Method 4     : 80 ms
>    Method 5ref  : 81 ms
>    Method 5noref: 82 ms
> ArrayOfStrings run 1 (loopcount 1000000)
>    Method 1     : 1 sec
>    Method 2     : 81 ms
>    Method 3     : 77 ms
>    Method 4     : 233 ms
>    Method 5ref  : 232 ms
>    Method 5noref: 223 ms
>
>
> (DMD Compiler on x86 with Optimization) "dmd -O dtostring.d"
> RuntimeString run 1 (loopcount 10000000)
>    Method 1     : 30 ms
>    Method 2     : 65 ms
>    Method 3     : 55 ms
>    Method 4     : 68 ms
>    Method 5ref  : 68 ms
>    Method 5noref: 67 ms
> StringWithPrefix run 1 (loopcount 1000000)
>    Method 1     : 158 ms
>    Method 2     : 9 ms
>    Method 3     : 8 ms
>    Method 4     : 63 ms
>    Method 5ref  : 64 ms
>    Method 5noref: 66 ms
> ArrayOfStrings run 1 (loopcount 1000000)
>    Method 1     : 1 sec, 292 ms
>    Method 2     : 35 ms
>    Method 3     : 34 ms
>    Method 4     : 193 ms
>    Method 5ref  : 198 ms
>    Method 5noref: 200 ms
>
> The results aren't suprising.  The template out performs the delegate
> sink.  In a very big project one might try to limit the number of
> instantiations of toString by using a specific toString instance that
> accepts some type common OutputRange wrapper which would make the
> template version perform the same as the sink delegate version, but for
> projects that don't need to worry about that, you will get better
> performance from more compiler optimization.

I think the performance gains are minimal. The only one that is 
significant is StringWithPrefix, which has a 11% gain. But that's still 
only 1ms, and 1ms on a PC can be attributed to external forces. I would 
increase the loop count on that one.

Note, if you really want to see gains, use -inline.

-Steve


More information about the Digitalmars-d mailing list