-
-
Notifications
You must be signed in to change notification settings - Fork 746
add std.internal.scopebuffer #1911
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
std/buffer/scopebuffer.d
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO make it a unittest example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added static assert for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no reason why the below example should not use a documented unit test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
newsgroup discussion: http://forum.dlang.org/post/ld2586$17f6$1@digitalmars.com |
std/buffer/scopebuffer.d
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this block indented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, do we want to document contracts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this block indented?
Because I thought it looked nicer.
In general, do we want to document contracts?
I don't find trivia to be helpful - in this case, any additional documentation would be trivia.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I thought it looked nicer.
I have not seen that anywhere else. It's not consistent with the reset of Phobos.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indenting it is also consistent with how constraints are used in templates, and is analogous in that they apply to the function parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indenting it is also consistent with how constraints are used in templates, and is analogous in that they apply to the function parameters.
It's not consistent with any other in-block. I would say that template constraints are put on the same line as the function declaration if the length of the line allows that. Otherwise they're put on a new line and indented, following the standard style guide for splitting up a single line.
The idea here was to get the entire struct into two registers on 64 bit code, which significantly improves performance. size_t is not necessary because it's hard to conceive of a stack allocated temporary buffer larger than 4 gigs. So there is a very good reason why it is uint. |
std/buffer/scopebuffer.d
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add
/// ditto
size_t opDollar()
{
return i;
}
here. "/// ditto
" is optional
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason why you didn't suggest alias opDollar = length;
here, as is usually done?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because most ranges are not containers, and only have size_t length()
. Containers, on the other hand, have void length(size_t desired)
. I think alias opDollar = length;
would work correctly in this context, but it could have some weird edge cases... On the other hand, I am 100% confident that re-writing opDollar
is correct 100% of the time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't spot the below overload, but opDollar
being an overload set still works, so I think it would only affect code that messes with .opDollar
explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added the alias. If that turns out to not work, will fix.
I like this: It can work as a nice deterministic but high performance alternative for Appender or Array. It has less "functionality" than both, so isn't bogged down by "non-features": The fact that i owns its buffer at all times should give it a real edge performance wise. Such an object was requested before as a "Real Appender" by @denis-sh : That said, I am concerned by the things complete lack of support for types with postblit, elaborate assignment or constructor. This is sad, because since because |
std/buffer/scopebuffer.d
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this had been a documented unit test, you'd find this doesn't compile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, my bad.
It's important in this case because it radically changes the interface. It goes from being safe and encapsulated to a leaky abstraction. |
@JakobOvrum: it is important but the proof has been made. |
Seems to me that I'm in the minority that has to prove things, while others don't. If it's the I feel like you're asking others to do your homework and show to see if's correct, because, of course, you know it all and doing it yourself has no point. Anyway, here the simplest I've come up with: If anything I'm seeing that ~this is consistently faster. You may point out any failures in this snippet. Some sample runs (compiled both files in one go with dmd -O -inline -release):
|
Wait, https://gist.github.com/blackwhale/9569368 uses |
@andralex I change it back and forth. |
Then of course, FIle also should avoid having a destructor, right? We'd be able to enregister that tiny struct! Less code and faster ;) But you'd agree that it makes no sense, precisely because the gains (if any) are minuscule compared to risk of a resource leak. I believe that anything that aims to be faster should have quantifiable benefits. Be it very specially crafted benchmark, or some metric derived with profiler or other tool. |
I agree we should add regular benchmarking as a matter of procedure to the toolchain. That said, this particular part of the discussion about proving performance improvements has run its course. I worked with Walter on said project. Yes, there are performance gains. Yes, in some cases they are spectacular. It's a memory buffer, which is very core. It doesn't take much expertise to figure that once enregistering happens, a lot of good stuff comes with it. I understand there are material objections to this module, so let's better focus on those. We can make it private to Phobos, move it to druntime, undocumment it and build a safer abstraction on top of it, etc. My own objection is that a very non-Phobosy module claims front and center stage position in std.buffer.scopebuffer. It should hang out in some internal/private/bits module. |
Great.
I see that I failed to deliver the message. Said Is it this request that hard to accommodate? What's wrong with you guys? Why constant appeal to authority and (Especially as these versions must be just different commits on the same source tree)
Nice. Well, uh-oh.
Something I can agree with. |
Before drifting away into detailed implementation discussions please help to clarify a few things. @Dicebot summarized my concern very nicely (#1911 (comment)).
It's just that I don't know how I could ever explain this mess to someone when advertising D. |
No need to get agitated. The fact of the matter is most everybody in our group, including probably yourself, has made changes to code that they argued and alleged it improved performance, and there was seldom a request for (or providing of) hard proof. The thing is, asking for hard proof without a systematic benchmarking framework is a tall order. One would need to build a synthetic benchmark that exercises code generation in similar ways as the real application, without pulling in a significant fraction of it. Once than, the few people on this review would be like, "mmkay, fine" and that entire work goes to nothing. All of that would change if we did have a systematic benchmarking framework. I'd say it's very productive to champion one and use this discussion as part of the motivating example. Asking for exhaustive proof here is, in my opinion, a bit much. I think concerns along the lines of @MartinNowak are the ones we need to address here. |
I think I should probably refrain from commenting on this, so my final remarks on subject of low-level optimization at all costs.
We did, and I always ask for it. How informal the reported numbers are varies wildly from pull to pull, but none I recall have come through solely on being theoretically solid.
Since you reported spectacular gains, obviously they are easy to observe else how would you confirm it in the first place? Since application in question obviously was benchmarked on some real work, then just use it as an indicator, that is all I asked. No It's just that I suspect (no proof, but test runs with my tiny snippet suggest) that the gains of having it w/o destructor are immeasurable.
Which indeed happens and no amount of benchmarking harness allows us to forget the common use case we optimize for, that is a benchmark after all. |
I don't understand this. Is it that Walter is lying if he doesn't tell you numbers? You refuse to take his word? He needs to sit down now and change code and collect numbers that show you things? What's this putting the hounds on someone all of a sudden whereas in all other case it's been like "yeah, fine, merge". |
FWIW the project will be open sourced soon and available for scrutiny. I still think this obstinate asking for evidence is not proportional response. |
OT: What project are you and Walter working on? Was it some kind of collaboration? Very interesting! |
I trust the machinery was done right and the code generated must be looking awesome. I don't easily trust that sacrificing usability was Putting things into perspective - I started this rant as explicitly removing destructor in publicly advertised primitive "that saves Phobos from GC leaks" is ehm in need of a good reason.
To put it simply - I don't know yet how much was brought in practice with trading away the destructor, and I failed to observe the gains myself. I do understand the significant loss in usability however. I thought ScopeBuffer would be a different primitive with wider scope ;) and simpler usage for generic code but I understand I can't affect that. Now that it is
So you don't trust that I naturally believed he did something like that before disabling the destructor? Well, alternatively he could have solely observed ASM listings and focused on the code generated. Telling just that would help me understand things. |
| What problem are you trying to solve? Using the stack for temporary buffers rather than storage allocation. Avoid generating garbage. Highest possible speed at doing things like string processing.
They're too slow.
Many phobos functions internally use GC allocations for temporary buffers, and then rely on a GC sweep to clean them up. These need to be replaced with ScopeBuffers as much as possible. std.file is a prime example. |
added to druntime |
Now moved to std.internal.scopebuffer |
win64.mak
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove this and make sure you test
Auto-merge toggled on |
This is missing the |
This adds a package std.buffer, and the first entry in that package, std.buffer.scopebuffer.
ScopeBuffer is an OutputRange that sits on the stack, and overflows to malloc/free. It's designed to help in eliminating gc allocation by lower level functions that return buffers, such as std.path.buildPath(). With some judicious user tuning of the initial stack size, it can virtually eliminate storage allocation.
Using it is @System, but the user of ScopeBuffer can be @trusted.
An output range like this is a precursor to eliminating the excessive gc use by functions such as buildPath().