Thread overview | ||||||||
---|---|---|---|---|---|---|---|---|
|
May 28, 2014 Cost of .dup vs. instantiation | ||||
---|---|---|---|---|
| ||||
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 Re: Cost of .dup vs. instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris | 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 Re: Cost of .dup vs. instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris | 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 Re: Cost of .dup vs. instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to monarch_dodra | 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 Re: Cost of .dup vs. instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris | 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 Re: Cost of .dup vs. instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to monarch_dodra | 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.
|
Copyright © 1999-2021 by the D Language Foundation