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