-
-
Notifications
You must be signed in to change notification settings - Fork 416
Expose a GC.statistics() function. #236
Conversation
Any input on this? The code itself is trivial, but what I really want to know is whether the amount of technical debt (and there's a lot of that here) this introduces is acceptable. |
I hadn't done anything about GC statistics yet because I wanted to have a discussion about exactly what metrics would be useful, and also consider how easy it would be to provide those across GC implementations. For impact, info gathering could be enabled/disabled via a flag. I mostly want to make sure that the bulk of GC implementations could provide the info whatever stats functionality we add exposes. |
I think that for the info that a GC implementation cannot provide, simply returning 0 or perhaps -1 is reasonable (documenting the lack of some stats feature(s) would also be a good idea). The thing about the statistics API is that it's only there for performance analysis and tuning, so strong guarantees about availability of information don't have to be made, IMO. Your thoughts on this? Also, how do you envision enabling/disabling gathering of statistics should work? |
I've put @complexmath on this. Regarding enabling/disabling statistics collection, I think we should have a property there. The big question is whether statistics could and should be enabled per thread or globally. |
Per-thread would be significantly more involved. I'm not opposed to it, but I think we can add per-thread statistics after we add support for global statistics gathering. @andralex @complexmath any input on my point about statistics that a GC cannot provide (i.e. just return 0)? Does it seem reasonable enough? |
A few more thoughts after making one more pass through this:
|
Oh, and the stats structure should have default initializers that don't make sense, e.g. fullCollections is currently 0 by default but it could actually be realistically zero. It should be size_t.max. |
Agree.
This sounds extremely complicated. Our build situation is already quite the maintenance nightmare...
I think it only makes sense to collect stats that are actually relevant to the GC implementation, however. There seems to be a lot of excess information in jemalloc that I am far from convinced anyone would ever need in a garbage-collected scenario.
I don't follow here. Any GC implementation can provide this particular piece of information, and, logically, they start out with zero collections? |
|
Sorry, you lost me there. Rephrase?
Sure, stuff like that should be there. I was just thinking that exposing every little piece of information in the statistics (which jemalloc seems to do - correct me if I'm wrong) might be a bit overkill.
That was actually just an artifact of code already in place inside the |
Rephrasing "My thought was just to define the gc_stats version and make all stats depend on it. No biggie. Leave this to only people who want to build their own druntime have it." -> "Let's define version=gc_stats; and then every action related to collecting statistics would go inside version(gc_stats) { ... } blocks." I agree that it's a bit onerous to require a versioned build to collect GC statistics. My opinion goes as follows:
We could go with (1) and not use versioning, but we'd be missing out on (2). Let me ask Jason Evans about how to best approach this. |
Oh, there's a 3rd way: use a global Boolean |
Yes, using a simple Boolean wouldn't really impact performance in any noticeable way and wouldn't complicate the build process. So I take it we'll have a |
Most likely, but let me get @jasone to weigh in on this before greenlighting. Thanks! |
I don't have any specific comments on the API under discussion, but here are some general thoughts about allocator statistics. As of jemalloc 3.0.0, all statistics gathering is enabled by default at build time. This adds a small amount of overhead, but without detailed statistics, jemalloc's behavior for diverse applications is very difficult to reason about. GC systems are even more sensitive to application behavior in my experience, so I would recommend gathering every informative statistic that doesn't impose an unacceptable overhead (i.e. constant-time overhead only), and leaving the statistics gathering machinery in place all the time. D's GC will never be "done", and the statistics will aid GC evolution. |
size_t pageBlocks; // number of blocks marked PAGE | ||
size_t fullCollections; // number of full collections | ||
} | ||
|
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.
Could we avoid defining this twice?
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.
Unfortunately, no. core.memory
cannot import any part of the gc
package due to visibility issues.
Closing this temporarily to avoid confusion. Will reopen once I do the discussed changes. |
Also, I just talked to a few folks on IRC, and they all seemed to think that simply returning zero for unsupported statistics is acceptable (and better from an API usability perspective), even if the statistic could realistically be zero. |
@andralex Ping. |
I'm against statistics that could be 0 either legally or when uncollected. This is a simple unforced design error, let's just fix it. |
We must still have a Boolean guard because it's the more extensible way - we don't know what statistics we'll add in the future, and some of those may actually be costlier than a jump. Also, people may disable and reenable statistics collection for measurement purposes (e.g. ignore the stats for certain portions of the code etc). Thanks. |
@andralex @complexmath New GC statistics interface is done. Please re-review the entire thing. |
|
||
extern (C) void gc_addRoot( in void* p ) nothrow; | ||
extern (C) void gc_addRange( in void* p, size_t sz ) nothrow; | ||
|
||
extern (C) void gc_removeRoot( in void* p ) nothrow; | ||
extern (C) void gc_removeRange( in void* p ) nothrow; | ||
|
||
extern (C) bool gc_getCollectStats() nothrow; |
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.
pure
? I'm only asking because I see you marked other functions as such.
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.
Wait, that's reading a global. Rats. I meant const
, but that doesn't quite apply :o).
struct BlockInfo | ||
{ | ||
void* base; /// A pointer to the base of the block in question. | ||
size_t size; /// The size of the block, calculated from base. |
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.
Instead of base+size, how about a void[]
?
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.
Sure.
I'm quite unhappy after reviewing this. It's a clusterfrak of functions forwarding to other functions, and work is actually done only in a fraction of the code written. There's some C-level functions that have awkward names, but then there's some D functions that also have awkward names. Though I fully understand that's not an issue introduced by this diff, I wonder how the hell did we get to this point. Any ideas on how to simplify this? |
I think we're putting the cart before the horse here. The reason I hadn't formally exposed statistics yet was because I didn't know which statistics were most useful and could be generated by most GC implementations. The existing statics were available only via a C call for this reason -- you could get them if you wanted them, but we hadn't made a commitment to the interface or the data returned. I'd like to see some actual discussion / research into what statistics would be most useful and worry about the implementation later. Andrei, it may be that the ultimate implementation does use a bunch of forwarding functions because this is how the GC interface works. It's primarily intended to avoid the need to import gc.anything. A separate issue might be whether it's reasonable to put an interface declaration in core.memory that a GC can expose. The C interface is really an artifact of earlier days when druntime was three logically separate sub-libraries. |
@andralex I fixed two of the issues you mentioned ( I don't think we can simplify the GC interface situation until we get proper shared libraries. The proxy stuff in particular seems to be a workaround for the lack of a proper shared GC. @complexmath I don't think there's such a thing as "could be generated by most GC implementations". Statistics will always be specific to the GC implementation; about the only statistic that is somewhat portable is Of course we can delay merging this pull request in favor of NG discussion if you really want, but I doubt such a discussion will reach any other conclusion than I did: A portable GC statistics interface is not very realistic. |
So, what do we do? Anyone want to start a discussion on the NG? |
@alexrp: will do |
Some wishes from the NG:
|
Second take on #233. Also includes a
fullCollections
property.