Before we start ripping apart our existing APIs, can we show that the performance is really going to be so bad? I know virtual calls have a bad reputation, but I hate to make these choices absent real data.

For instance, D's underlying i/o system uses FILE *, which is about as virtual as you can get. So are you avoiding a virtual call to use a buffer to then pass to a virtual call later?

I think its debatable how useful this information would be but I've written a small D Program to try to explore the different performance statistics for various methods. I've uploaded the code to my server, feel free to download/modify/use.

Here's the various methods I've tested.
/**
   Method 1: ReturnString
             string toString();
   Method 2: SinkDelegate
             void toString(void delegate(const(char)[]) sink);
   Method 3: SinkDelegateWithStaticHelperBuffer
struct SinkStatic { char[64] buffer; void delegate(const(char)[]) sink; }
             void toString(ref SinkStatic sink);
   Method 4: SinkDelegateWithDynamicHelperBuffer
struct SinkDynamic { char[] buffer; void delegate(const(char)[]) sink; }
             void toString(ref SinkDynamic sink);
             void toString(SinkDynamic sink);

 */

Dmd/No Optimization (dmd dtostring.d):

RuntimeString run 1 (loopcount 10000000)
  Method 1     : 76 ms
  Method 2     : 153 ms
  Method 3     : 156 ms
  Method 4ref  : 157 ms
  Method 4noref: 167 ms
StringWithPrefix run 1 (loopcount 1000000)
  Method 1     : 159 ms
  Method 2     : 22 ms
  Method 3     : 80 ms
  Method 4ref  : 80 ms
  Method 4noref: 83 ms
ArrayOfStrings run 1 (loopcount 1000000)
  Method 1     : 1 sec and 266 ms
  Method 2     : 79 ms
  Method 3     : 217 ms
  Method 4ref  : 226 ms
  Method 4noref: 222 ms

Dmd/With Optimization (dmd -O dtostring.d):
RuntimeString run 1 (loopcount 10000000)
  Method 1     : 35 ms
  Method 2     : 67 ms
  Method 3     : 67 ms
  Method 4ref  : 72 ms
  Method 4noref: 70 ms
StringWithPrefix run 1 (loopcount 1000000)
  Method 1     : 154 ms
  Method 2     : 9 ms
  Method 3     : 86 ms
  Method 4ref  : 63 ms
  Method 4noref: 65 ms
ArrayOfStrings run 1 (loopcount 1000000)
  Method 1     : 1 sec and 252 ms
  Method 2     : 37 ms
  Method 3     : 191 ms
  Method 4ref  : 193 ms
  Method 4noref: 201 ms


I would like to make a note that passing around a stack allocated buffer to the various toString methods along with a sink delegate may not get much performance benefit. One reason is because the logic can get a little hairy when trying to decide if the buffer is large enough for the string (see my code on ArrayOfStrings) which creates more code which can slow down the processor simply because there is more "code" memory the processor has to manage. Also note that adding a helper method to the buffer/sink delegate doesn't help at all since this is equivalent to passing around the delegate (meaning you could just create the Buffered sink that calls the real delegate when it gets full and pass around it's own sink delegate).

Reply via email to