Implement a monitor system for the GC#2052
Implement a monitor system for the GC#2052mathias-lang-sociomantic wants to merge 1 commit intodlang:masterfrom mathias-lang-sociomantic:gc_monitor
Conversation
Allow one to hook the start and end of a fullcollect, potentially gathering statistics / logging the event as one see fit. This was present in the D1 runtime but got stripped for unknown reason (git history has no mention of it). Sociomantic relies on it heavily for some real-time apps.
|
Thanks for your pull request, @mathias-lang-sociomantic! Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. |
|
|
||
| // Alias used because DMD thinks the delegate are extern(C) as well otherwise | ||
| alias CollectionStartHook = void delegate() nothrow @nogc; | ||
| alias CollectionEndHook = void delegate(size_t, size_t) nothrow @nogc; |
There was a problem hiding this comment.
@mihails-strasuns-sociomantic mentioned that this could be two instances of the GC.Stats struct.
However, those are not so cheap to get, since we iterate over the pools (unless I misunderstood something).
Given the use cases for those hooks, I think having a no-cost approach is quite mandatory.
There was a problem hiding this comment.
I think having a no-cost approach is quite mandatory
It still has to hold GC lock which immediately makes it pretty far from no-cost.
There was a problem hiding this comment.
I think acquiring a lock (specially in single threaded environments, where is basically an atomic op usually) is considered no-cost compared to traverse the whole pool set. Also, there seems to be no reason to do it when the user can get the stats anyway with just one extra call, if needed.
There was a problem hiding this comment.
I have two unrelated concerns about it:
-
It is not cheap in multi-threaded environments and while single-threaded optimization is indeed most suitable to our needs, druntime version should be more uniformly useful.
-
To avoid calculating exact stats this PR right now exposes
freedLargePagesandfreedSmallPagesas monitored values and I would argue those alone have rather limited usefulness.
What would be really cool is to accept different types of callback delegates and provide required stats based on what is desired by callback. Not sure if it can fit into API elegantly though.
|
|
||
| /// Hook used for debugging application-side | ||
| __gshared CollectionStartHook on_collect_start; | ||
| __gshared CollectionEndHook on_collect_end; |
There was a problem hiding this comment.
One point raised by @mihails-strasuns-sociomantic is that having delegate + __gshared is dangerous. Since we have the lock it should be okay, but I'm interested to know if there's something addressing this issue anywhere else.
There was a problem hiding this comment.
Well, from pure theoretical PoV it should require shared delegate but I still have no idea if that is even implemented to mean something in D frontend.
leandro-lucarella-sociomantic
left a comment
There was a problem hiding this comment.
I guess this also needs a test. At least the most simple form of it should be easy to do (just register the handlers that write to some global, call GC.collect() and check if the handlers were ran), at least if there is any infrastructure for this.
|
|
||
| // Alias used because DMD thinks the delegate are extern(C) as well otherwise | ||
| alias CollectionStartHook = void delegate() nothrow @nogc; | ||
| alias CollectionEndHook = void delegate(size_t, size_t) nothrow @nogc; |
There was a problem hiding this comment.
I think acquiring a lock (specially in single threaded environments, where is basically an atomic op usually) is considered no-cost compared to traverse the whole pool set. Also, there seems to be no reason to do it when the user can get the stats anyway with just one extra call, if needed.
| * Useful for debugging and tuning. | ||
| */ | ||
| void monitor (CollectionStartHook on_start, CollectionEndHook on_end) | ||
| nothrow @nogc; |
There was a problem hiding this comment.
@__future?
There was a problem hiding this comment.
That makes sense.
I would be extremely surprised if someone was implementing this interface, but I suppose that's a good test anyway.
There was a problem hiding this comment.
Yeah, I know it would be extremely unlikely, but this is a good example for what future was created, to avoid any kind of surprises as much as possible, and OTOH we know people is creative and we always get surprised about how assertions like "probably nobody is doing X" prove themselves wrong with time :)
There was a problem hiding this comment.
I don't think @future makes any sense here - gc package is not available to import, it is strictly contained within druntime. So there is no downstream to care about.
| /// Provided for implementation's convenience, not intended for public usage | ||
| protected alias CollectionStartHook = void delegate() nothrow @nogc; | ||
| /// Ditto | ||
| protected alias CollectionEndHook = void delegate(size_t, size_t) nothrow @nogc; |
There was a problem hiding this comment.
@__future? Although I don't think it's working for aliases.
There was a problem hiding this comment.
I don't think that's necessary for alias actually. If someone defines something with the same name, it will just shadow this.
|
BTW, all tests are failing, looks like a linking problem, at lease in jenkins: |
|
Nice, I have a few uses already. One thing needed would be composability - a call to monitor should not overwrite the previous call. So we're looking at a singly-linked list of hooks. There's some nice related work in the area: |
Is there any advantage of doing it within the GC ? To implement this approach, I would suggest using this simple hooking mechanism and simply providing a delegate that forwards: struct HookList (Dg)
{
private LinkedList!(Dg) hooks;
public void callback (size_t a, size_t b)
{
foreach (h; this.hooks)
h.call(a, b);
}
}
/// ...
HookList!(void delegate(size_t, size_t) hook;
GC.monitor(null, &hook.callback);This way the GC implementation stays simple, and we leave open an unlimited realm of behavior. |
|
I agree is good to keep the complexity out of the GC/runtime if possible. Is true that if some library want to hook to the GC monitor, then things get hairy, as user code implementing this kind of hooking mechanism won't be enough. But then this is a pretty low level construct and one might argue that probably a library shouldn't be hooking to this, but providing the user a callback in case the user want to hook it. |
|
This could get modularized in a number of ways. At any rate there should be a public API that allows code to add hooks without clobbering existing hooks. One more possibility that comes to mind is what some Windows APIs do - pass the "previous" delegate as an argument to the hook. Then the hook may decide cooperatively to continue down the chain by calling the hook, or stop the chain. |
|
Yeah, a way to get the current hooks might be the easiest way to enable users to do the chain manually if appropriate. |
|
Threads are likely to make matters difficult for a get/set interface backed by globals (currently there's only means for setting).
|
|
There is also another multi-threaded concern (referenced here #2052 (comment)) - hook list stores delegates that capture some thread context and thus executing such delegate may have a race with a thread that registered it originally over the very same context. Basically |
|
@mihails-strasuns-sociomantic wouldn't all hooks be invoked from the collection thread with all other threads frozen? |
|
@andralex you are correct of course - and unregistering thread from runtime is |
|
Useful addition. I did something similar in my early GC adventures.
|
|
Just to recap, what would be the minimum requirements for this PR to be accepted? |
|
Once the feature is in, there will be a healthy demand for its modular use. Thinking of things like caches, memoization, weak pointers, files to close, etc. The list would include:
An eye for applications would be great. I'm thinking assertions ("this object is no longer referenced") and caches. |
|
Thanks for your feedback @andralex . This will be taken over by someone else in the future. |
|
We kind of did a survey on our applications to see how this facility was used, and it was almost exclusively used to get how much time was spent doing collections. The list of requirements seem to be super reasonable and I think we should go that way if this gets ever implemented, but actually we are going to switch the focus to improve GC stats instead (we'll go more on the path of #1846), which is what we really need. |
Allow one to hook the start and end of a fullcollect, potentially gathering statistics / logging the event as one see fit.
This was present in the D1 runtime but got stripped for unknown reason (git history has no mention of it).
Sociomantic relies on it heavily for some real-time apps.