Thread overview
Cost of .dup vs. instantiation
May 28, 2014
Chris
May 28, 2014
monarch_dodra
May 29, 2014
Chris
May 29, 2014
monarch_dodra
May 29, 2014
Chris
May 28, 2014
TheFlyingFiddle
May 28, 2014
I use Appender to fill an array. The Appender is a class variable and is not instantiated with each function call to save instantiation. However, the return value or the function must be dup'ed, like so:

Appender!(MyType[]) append;
public auto doSomething() {
  scope (exit) { // clear append }
  // ... do something
  append ~= item;
  return (append.data).dup
}

My question is whether I save anything with Appender as a class variable here. I have to .dup the return value (+ clear the Appender). If I had a new Appender with each function call, it might be just as good.

public auto doSomething() {
  Appender!(MyType[]) append;
  // ....
  return append.data.
}

Right or wrong?

May 28, 2014
On Wednesday, 28 May 2014 at 14:36:25 UTC, Chris wrote:
> I use Appender to fill an array. The Appender is a class variable and is not instantiated with each function call to save instantiation. However, the return value or the function must be dup'ed, like so:
>
> Appender!(MyType[]) append;
> public auto doSomething() {
>   scope (exit) { // clear append }
>   // ... do something
>   append ~= item;
>   return (append.data).dup
> }
>
> My question is whether I save anything with Appender as a class variable here. I have to .dup the return value (+ clear the Appender). If I had a new Appender with each function call, it might be just as good.
>
> public auto doSomething() {
>   Appender!(MyType[]) append;
>   // ....
>   return append.data.
> }
>
> Right or wrong?

You might save a little because you avoid the cost of "growing" your appender repeatedly: Once the appender has come to "maturity", it will very likely stop growing.

At that point, you only pay for *1* allocation per call to doSomething. Further advantages include:
 - dup has "APPENDABLE" info (whereas appender.data does not).
 - less wasted memory: dup uses no more memory than it has to, whereas Appender may over-allocate, depending on how you fill it.

The "downside" to your approach is that you keep a handle on a buffer that can grow, but never shrink. If a at a certain point, you have to process some particularly large input, then you'll consume excessive amounts of memory.
May 28, 2014
On Wednesday, 28 May 2014 at 14:36:25 UTC, Chris wrote:
> I use Appender to fill an array. The Appender is a class variable and is not instantiated with each function call to save instantiation. However, the return value or the function must be dup'ed, like so:
>
> Appender!(MyType[]) append;
> public auto doSomething() {
>   scope (exit) { // clear append }
>   // ... do something
>   append ~= item;
>   return (append.data).dup
> }
>
> My question is whether I save anything with Appender as a class variable here. I have to .dup the return value (+ clear the Appender). If I had a new Appender with each function call, it might be just as good.
>
> public auto doSomething() {
>   Appender!(MyType[]) append;
>   // ....
>   return append.data.
> }
>
> Right or wrong?

When it comes to optimizations it's hard to say. Benchmarking is better than relying advice/opinions on the internet in any case.

That being said i doubt that the instantiation cost of the Appender is relevant. (Btw the appender is not a class variable! It is a struct with reference semantics). Reusing an appender is more for those cases where you want to reuse the underlying memory of the appender itself.

May 29, 2014
On Wednesday, 28 May 2014 at 17:33:19 UTC, monarch_dodra wrote:
> On Wednesday, 28 May 2014 at 14:36:25 UTC, Chris wrote:
>> I use Appender to fill an array. The Appender is a class variable and is not instantiated with each function call to save instantiation. However, the return value or the function must be dup'ed, like so:
>>
>> Appender!(MyType[]) append;
>> public auto doSomething() {
>>  scope (exit) { // clear append }
>>  // ... do something
>>  append ~= item;
>>  return (append.data).dup
>> }
>>
>> My question is whether I save anything with Appender as a class variable here. I have to .dup the return value (+ clear the Appender). If I had a new Appender with each function call, it might be just as good.
>>
>> public auto doSomething() {
>>  Appender!(MyType[]) append;
>>  // ....
>>  return append.data.
>> }
>>
>> Right or wrong?
>
> You might save a little because you avoid the cost of "growing" your appender repeatedly: Once the appender has come to "maturity", it will very likely stop growing.
>
> At that point, you only pay for *1* allocation per call to doSomething. Further advantages include:
>  - dup has "APPENDABLE" info (whereas appender.data does not).
>  - less wasted memory: dup uses no more memory than it has to, whereas Appender may over-allocate, depending on how you fill it.
>
> The "downside" to your approach is that you keep a handle on a buffer that can grow, but never shrink. If a at a certain point, you have to process some particularly large input, then you'll consume excessive amounts of memory.

monarch_dodra:
Hm. This last point might be an issue. If I process a large input (text in this case) then I might run into trouble with "append" as a class variable. I also had a weird bug, because I didn't clear the memory for overwrite.

TheFlyingFiddle:
"append" is a class variable in _my_ program, not Appender, like so:

class A {
  Appender!(MyType[]) append;
  // ...
}
May 29, 2014
On Thursday, 29 May 2014 at 08:49:10 UTC, Chris wrote:
> monarch_dodra:
> Hm. This last point might be an issue. If I process a large input (text in this case) then I might run into trouble with "append" as a class variable. I also had a weird bug, because I didn't clear the memory for overwrite.

You can always implement an "upper bound" approach, where if your input data becomes larger than a certain size, you return the data directly, and reset your appender. EG:

Appender!(MyType[]) append;
public auto doSomething() {
  scope (failure) { append.clear; }
  // ... do something
  append ~= item;
  MyType[] ret;
  if (append.data.length < 10_000)
  {
    ret = append.data).dup;
    append.clear; //clears buffer, keeps memory.
  }
  else
  {
    ret = append.data;
    append = appender!(MyType[])(); //jettison old appender data.
  }
  return ret;
}
May 29, 2014
On Thursday, 29 May 2014 at 12:04:35 UTC, monarch_dodra wrote:
> On Thursday, 29 May 2014 at 08:49:10 UTC, Chris wrote:
>> monarch_dodra:
>> Hm. This last point might be an issue. If I process a large input (text in this case) then I might run into trouble with "append" as a class variable. I also had a weird bug, because I didn't clear the memory for overwrite.
>
> You can always implement an "upper bound" approach, where if your input data becomes larger than a certain size, you return the data directly, and reset your appender. EG:
>
> Appender!(MyType[]) append;
> public auto doSomething() {
>   scope (failure) { append.clear; }
>   // ... do something
>   append ~= item;
>   MyType[] ret;
>   if (append.data.length < 10_000)
>   {
>     ret = append.data).dup;
>     append.clear; //clears buffer, keeps memory.
>   }
>   else
>   {
>     ret = append.data;
>     append = appender!(MyType[])(); //jettison old appender data.
>   }
>   return ret;
> }

Yes, you're right, this is a possible solution. However, I don't feel good about keeping memory I don't need. This might be a problem when the code will finally be ported to mobile devices.

I benchmarked the two implementations (class variable vs local variable) and the performance (i.e. speed) remains the same.