-
Notifications
You must be signed in to change notification settings - Fork 284
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
MatchGraphBuilder.disambiguate eats my memory #1359
Comments
Ah, you've finally found it. I've resorted to falling back to the old tree matching algorithm a while back because of an infinite loop issue in there :-p |
hmm after some hours the real leak is actually in somewhere else. this is how it looks after some 10 hours:
|
Do you have the GC enabled? I was under the impression that everything done in |
yes GC enabled |
We deploy to low resource servers and this is a major issue for us right now. Note: I am still on 0.7.25 |
Hm, I'm not sure how to read that log there. If the GC is enabled, am I right in that those numbers are the total numbers and not the current memory use? All I can say to this with the current information is that I'm 99.9% certain that all memory claimed by |
I upgraded to 0.7.28 recently and since VibeOldRouterImpl is broken on this release, I now use the match tree. It seems the infinite loop is gone, likely to be due to a related fix in DMD, however, the disambiguate() call still takes 13secs for my router setup, which is pretty nasty for development. Could we maybe switch to an incremental implementation if it's faster? Or at the very least, could we bring back VibeOldRouterImpl? @s-ludwig could you describe a bit the disambiguate() implementation, or perhaps point me in the direction of any references you used during the implementation? It's not easy for me to understand what the code is trying to do and how. |
An incremental implementation would not be efficient, but the existing function, I'm pretty sure, can be optimized a lot. I'm currently looking into that. |
- Changes the memory layout to store NFA edges - Pre-allocates memory for combined edge lists - Uses simple append instead of addToEdge where possible
Okay, got a 5x increase in speed for a synthetic test with 1400 partially overlapping routes (~20s down to ~4s). Further improvements will be more difficult, but I hope that this is enough for all usual real world cases. |
Avoids GC allocations for tiny arrays. See #1359.
Release build gives another 2x increase. There are some low-hanging fruits, such as avoiding |
Wow! Great @s-ludwig Thanks for this! |
Came across this too. vibe-0.7.30-alpha4 Here is the output from profile-gc just after the first call:
|
It seems like the GC implementation doesn't count actual allocations, but also re-allocations of already allocated blocks. That spot uses
This appears to be the only valid large long-term allocation. However, the amount of memory would amount to over 200000 graph nodes, which means over around 2500 characters per route for 80 routes, which doesn't seem right. What I suspect is that the profile-gc output contains a lot of old allocations that have already been collected (although I'm not familiar with this). The process generally wouldn't go below 200MB after having used that much once, because the GC simply never gives back memory to the OS - or at least of that hasn't changed recently. If this is all true, it's likely that this line is the one responsible for allocating almost 200MB in the first place: To clear up some unknowns, can you post the router log output for |
I've tried to create some sample app to show the behaviour. It outputs: Match tree has 3467 nodes, 201 terminals With our original (closed source unfortunatelly) service it outputs: Match tree has 348 nodes, 288 terminals. profilegc output is pretty similar. As I looked through the router.d it seems to me that node_stack handling in disambiguate method must reallocate too much. Could this be the problem? |
Seemingly doesn't affect the real process usage, but cleans up the -profile=gc output of DMD.
All profile log entries turned out to be red herrings, all caused by appending to a dynamic array. The array only grew in place, but obviously the GC stats collector adds the full array length each time. I've eliminated two out of three now and the remaining one is not enough to explain the memory usage. So I see two possibilities left now:
I'll investigate further when I get some time. |
I've updated the test to use Because of that the URLRouter is created as many times as I have virtual CPUs, which leads on my 8 virtual CPUs to these numbers:
If I add: GC.collect();
GC.minimize() I get:
Still pretty crazy for a service which does nothing. |
Okay, this is definitely caused by memory fragmentation. I replaced all arrays in |
Working version: https://github.com/vibe-d/vibe.d/tree/router_mem_opt |
dependency "vibe-d" version="0.7.26"
after a short while of production traffic i have huge memory usage.
-profile=gc shows
MatchGraphBuilder.disambiguate
as the culpritthe only non fully defined route is this:
router.get("*", serveStaticFiles("public"));
The text was updated successfully, but these errors were encountered: