August 14, 2014 Appender is ... slow | ||||
---|---|---|---|---|
| ||||
From time to time, I try to speed up some array-heavy code by using std.array.Appender, reserving some capacity and so on. It never works. Never. It gives me executables that are maybe 30-50% slower than bog-standard array code. I don't do anything fancy: I just replace the types, use clear() instead of = null... Do people here get good results from Appender? And if yes, how are you using it? |
August 14, 2014 Re: Appender is ... slow | ||||
---|---|---|---|---|
| ||||
Posted in reply to Philippe Sigaud | On Thursday, 14 August 2014 at 17:16:42 UTC, Philippe Sigaud wrote:
> From time to time, I try to speed up some array-heavy code by using std.array.Appender, reserving some capacity and so on.
>
> It never works. Never. It gives me executables that are maybe 30-50% slower than bog-standard array code.
>
> I don't do anything fancy: I just replace the types, use clear() instead of = null...
>
> Do people here get good results from Appender? And if yes, how are you using it?
I don't know much about Phobos appender implementation details but the key thing with reusable buffer is avoid freeing them. AFAIR Appender.clear frees the allocated memory but `Appender.length = 0` does not, making it possible to just overwrite stuff again and again.
Won't promise you anything though!
|
August 14, 2014 Re: Appender is ... slow | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | > I don't know much about Phobos appender implementation details but the key thing with reusable buffer is avoid freeing them. AFAIR Appender.clear frees the allocated memory but `Appender.length = 0` does not, making it possible to just overwrite stuff again and again.
I call .clear() only at the beginning of the computation, to avoid any
strange effects with std.datetime.benchmark (using benchmark with
memoizing functions can lead to strange results if one does not take
care to flush any result between to calls.)
After that initial call to clear, I just append.
The thing is, it's not the first time it happens. For years, I tried to use Appender to get faster code, to no avail.
btw, I saw your Dconf talk yesterday, nice content! And thanks for
talking about Pegged!
It might interest you to know that the code I'm trying to use Appender
on is a new engine for Pegged, based on GLL parsing, that should be
able to produce a parser for any grammar, even ambiguous ones.
|
August 14, 2014 Re: Appender is ... slow | ||||
---|---|---|---|---|
| ||||
Posted in reply to Philippe Sigaud | On Thursday, 14 August 2014 at 17:16:42 UTC, Philippe Sigaud wrote:
> From time to time, I try to speed up some array-heavy code by using std.array.Appender, reserving some capacity and so on.
>
> It never works. Never. It gives me executables that are maybe 30-50% slower than bog-standard array code.
>
> I don't do anything fancy: I just replace the types, use clear() instead of = null...
>
> Do people here get good results from Appender? And if yes, how are you using it?
I've never really tried to benchmark it, but it was my understanding that the idea behind Appender was to use it to create the array when you do that via a lot of appending, and then you use it as a normal array and stop using Appender. It sounds like you're trying to use it as a way to manage reusing the array, and I have no idea how it works for that. But then again, I've never actually benchmarked it for just creating arrays via appending. I'd just assumed that it was faster than just using ~=, because that's what it's supposedly for. But maybe I just completely misunderstood what the point of Appender was.
- Jonathan M Davis
|
August 14, 2014 Re: Appender is ... slow | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | > I've never really tried to benchmark it, but it was my understanding that the idea behind Appender was to use it to create the array when you do that via a lot of appending, and then you use it as a normal array and stop using Appender. That's how I use it, yes. > It sounds like you're trying to use it as a way to manage reusing the array, and I have no idea how it works for that. There is a misunderstanding there: I'm using clear only to flush the state at the beginning of the computation. The Appender is a class field, used by the class methods to calculate. If I do not clear it at the beginning of the methods, I keep appending new results to old computations, which is not what I want. But really, calling clear is a minor point: I'm interested in Appender's effect on *one* (long, concatenation-intensive) computation. > I've > never actually benchmarked it for just creating arrays via appending. I'd > just assumed that it was faster than just using ~=, because that's what it's > supposedly for. But maybe I just completely misunderstood what the point of > Appender was. I don't know. People here keep telling newcomers to use it, but I'm not convinced by its results. Maybe I'm seeing worse results because my arrays are do not have millions of elements and Appender shines for long arrays? |
August 14, 2014 Re: Appender is ... slow | ||||
---|---|---|---|---|
| ||||
Posted in reply to Philippe Sigaud | On Thursday, 14 August 2014 at 19:29:28 UTC, Philippe Sigaud via Digitalmars-d-learn wrote: >> It sounds like you're trying to use it as a way to manage reusing >> the array, and I have no idea how it works for that. > > There is a misunderstanding there: I'm using clear only to flush the > state at the beginning of the computation. The Appender is a class > field, used by the class methods to calculate. If I do not clear it at > the beginning of the methods, I keep appending new results to old > computations, which is not what I want. But really, calling clear is a > minor point: I'm interested in Appender's effect on *one* (long, > concatenation-intensive) computation. Then it sounds like you're reusing the Appender. I've never done that. In fact, I would have assumed that that would mean that you were attempted to fill in the same array again, and I wouldn't have even thought that that was safe, because I would have assumed that Appnder used assumeSafeAppend, which would mean that reusing the Appender would be highly unsafe unless you weren't using the array that you got from it anymore. I always use Appender to construct an array, and then I get rid of the Appender. I don't think that I've ever had a member variable which was an Appender. I only ever use it for local variables or function arguments. >> I've >> never actually benchmarked it for just creating arrays via appending. I'd >> just assumed that it was faster than just using ~=, because that's what it's >> supposedly for. But maybe I just completely misunderstood what the point of >> Appender was. > > I don't know. People here keep telling newcomers to use it, but I'm > not convinced by its results. Maybe I'm seeing worse results because > my arrays are do not have millions of elements and Appender shines for > long arrays? I have no idea. It was my understandnig that it was faster to create an array via appending using Appender than ~=, but I've never benchmarked it or actually looked into how it works. It's quite possible that while it's _supposed_ to be faster, it's actually flawed somehow and is actually slower, and no one has noticed previously, simply assuming that it was faster because it's supposed to be. - Jonathan M Davis |
August 14, 2014 Re: Appender is ... slow | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On Thursday, 14 August 2014 at 19:10:18 UTC, Jonathan M Davis wrote:
> I've never really tried to benchmark it, but it was my understanding that the idea behind Appender was to use it to create the array when you do that via a lot of appending, and then you use it as a normal array and stop using Appender. It sounds like you're trying to use it as a way to manage reusing the array, and I have no idea how it works for that. But then again, I've never actually benchmarked it for just creating arrays via appending. I'd just assumed that it was faster than just using ~=, because that's what it's supposedly for. But maybe I just completely misunderstood what the point of Appender was.
>
> - Jonathan M Davis
I too have trouble understanding what Appender does that supposedly makes it faster (at least from the documentation). My old, naive thought was that it was something like a linked list of fixed size arrays so that appends didn't have to move existing elements until you were done appending, at which point it would bake it into a regular dynamic array moving each element only once looking at the code it appeared to be nothing like that (an std::deque with a copy into a vector in c++ terms).
Skimming the code it appears to be more focused on the much more basic "~= always reallocates" performance problem. It seems it boils down to doing essentially this (someone feel free to correct me) in the form of an output range:
auto a = /* some array */;
auto b = a;
a = a.array();
for(...)
b.assumeSafeAppend() ~= /* element */;
(assumeSafeAppend's documentation doesn't say whether or not it'll reallocate when capacity is exhausted, I assume it does).
|
August 14, 2014 Re: Appender is ... slow | ||||
---|---|---|---|---|
| ||||
Posted in reply to Philippe Sigaud | On Thursday, 14 August 2014 at 19:29:28 UTC, Philippe Sigaud via Digitalmars-d-learn wrote:
> There is a misunderstanding there: I'm using clear only to flush the
> state at the beginning of the computation. The Appender is a class
> field, used by the class methods to calculate. If I do not clear it at
> the beginning of the methods, I keep appending new results to old
> computations, which is not what I want. But really, calling clear is a
> minor point: I'm interested in Appender's effect on *one* (long,
This is exactly what I propose to change - set `length` to 0 instead of calling `clear`. That way you will keep using same memory chunk simply abandoning its old state at the beginning of each computation.
|
August 14, 2014 Re: Appender is ... slow | ||||
---|---|---|---|---|
| ||||
Posted in reply to Philippe Sigaud | On Thursday, 14 August 2014 at 18:55:55 UTC, Philippe Sigaud via Digitalmars-d-learn wrote:
> btw, I saw your Dconf talk yesterday, nice content! And thanks for
> talking about Pegged!
> It might interest you to know that the code I'm trying to use Appender
> on is a new engine for Pegged, based on GLL parsing, that should be
> able to produce a parser for any grammar, even ambiguous ones.
Thanks! Repeating what I have mentioned during DConf talk - have you ever considered proposing Pegged for Phobos inclusion? It feels like important bit of infrastructure to me.
|
August 14, 2014 Re: Appender is ... slow | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | >> There is a misunderstanding there: I'm using clear only to flush the state at the beginning of the computation. The Appender is a class field, used by the class methods to calculate. If I do not clear it at the beginning of the methods, I keep appending new results to old computations, which is not what I want. But really, calling clear is a minor point: I'm interested in Appender's effect on *one* (long,
>
>
> This is exactly what I propose to change - set `length` to 0 instead of calling `clear`. That way you will keep using same memory chunk simply abandoning its old state at the beginning of each computation.
You mean by using the shrinkTo method? (Appender does not have a
length, that's a bit of a bother btw).
I just did, it does not change anything. Too bad.
Heck, my code is simpler to read and use *and* faster by using a bog-standard D array. I'll keep my array for now :)
|
Copyright © 1999-2021 by the D Language Foundation