StreamSelectLoop: Improve memory consumption and runtime performance for timers#164
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
While debugging some very odd memory issues in a live application, I noticed that the
StreamSelectLoopdoes not immediately free all related memory when a timer is cancelled. Let's not call this a "memory leak", because memory was eventually freed, but this clearly caused some unexpected and significant memory growth.In many common programs, adding timeouts to operations is a very fundamental operation. As such, it's common to end up with hundred or thousands of timers which most of the time get cancelled very frequently as a successful execution will quickly cancel outstanding timeouts.
I've used the following script to demonstrate unreasonable memory growth:
Running this on the current master branch, this peaked at around 320 MB of memory on my system (YMMV). After applying this patch, this script reports a constant memory consumption of around 0.7 MB.
The original implementation internally relied on a
SplPriorityQueuefor fast insertions of new timers, but this class lacks access to actually remove timers. This means that they would actually stay in the queue until they were originally supposed to fire.This was apparently done for performance reasons, so this patch includes some thorough performance tests. The result here is that adding a large number of timers at the same time shows a significant performance improvement (
time php examples/92-benchmark-timers.php 20000from 15s down to 2s). Waiting for a large number of timers to fire one after another does not show a noticeable impact (time php examples/94-benchmark-timers-delay.php 20000both at around 2.5s).