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