www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Cost of .dup vs. instantiation

reply "Chris" <wendlec tcd.ie> writes:
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
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
parent reply "Chris" <wendlec tcd.ie> writes:
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
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
parent "Chris" <wendlec tcd.ie> writes:
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.
May 29 2014
prev sibling parent "TheFlyingFiddle" <kurtyan student.chalmers.se> writes:
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 28 2014