toString refactor in druntime

Jonathan Marler via Digitalmars-d digitalmars-d at puremagic.com
Mon Nov 3 15:05:24 PST 2014


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.

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.



More information about the Digitalmars-d mailing list