-
-
Notifications
You must be signed in to change notification settings - Fork 127
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
StreamSelectLoop: Improve memory consumption and runtime performance for timers #164
Conversation
while (!$scheduler->isEmpty()) { | ||
$timer = $scheduler->top(); | ||
// ensure timers are sorted so we can execute in order | ||
if (!$this->sorted) { |
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.
You have the exact same logic at line 54. You could create a private method ensureScheduleIsSorted()
or something similar in order to do this checking and perform the sorting operation.
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.
Fair point! This is the very core of the event loop, so this is the critical code path and I would rather avoid any additional function calls. What do you think about this? 👍
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.
Interesting to point out. Make sense to me 👍
SPL data structures are so badly written that I'm not at all surprised that their performance is not good. But I'd really like to see whether the new Data Structures would be better. In theory they should be faster than arrays since arrays have too many features to be efficient. The new Data Structures have 2 implementations. One as PHP extension and another as pure PHP composer package (useful as fallback in case you can't install the extension). |
@nawarian Thank you for the review! I've addressed all things you've pointed out and believe this is ready to be merged. 👍 @enumag While I don't agree with this assessment about SPL, I very much appreciate the discussion about DS! I think it may make sense to look into this for a future version, perhaps you or somebody else feels like filing a PR for this in the future so we can evaluate this? 👍 One of the design goals for this component is that is runs everywhere and is standalone without any external dependencies, so I'm curious how this would work out eventually. |
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.
lgtm
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.
👍 and thanks @nawarian for the review!
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.
👍 and thanks for the review @nawarian 🖖
While debugging some very odd memory issues in a live application, I noticed that the
StreamSelectLoop
does 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
SplPriorityQueue
for 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 20000
from 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 20000
both at around 2.5s).