digitalmars.D.learn - Cost of .dup vs. instantiation
- Chris (21/21) May 28 2014 I use Appender to fill an array. The Appender is a class variable
- monarch_dodra (13/34) May 28 2014 You might save a little because you avoid the cost of "growing"
- Chris (13/52) May 29 2014 monarch_dodra:
- monarch_dodra (22/27) May 29 2014 You can always implement an "upper bound" approach, where if your
- Chris (6/34) May 29 2014 Yes, you're right, this is a possible solution. However, I don't
- TheFlyingFiddle (8/29) May 28 2014 When it comes to optimizations it's hard to say. Benchmarking is
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 17:33:19 UTC, monarch_dodra wrote:On Wednesday, 28 May 2014 at 14:36:25 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. TheFlyingFiddle: "append" is a class variable in _my_ program, not Appender, like so: class A { Appender!(MyType[]) append; // ... }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 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: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.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 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