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

Reply via email to