windows: use a heap to hold timers on windows, #1460 #1476
Conversation
|
||
if (a->start_id < b->start_id) | ||
return 1; |
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.
style: use 2 spaces for indenting
Left some comments, but it looks solid, thanks! |
i have updated it according to your comments |
|
||
timer = container_of(heap_node, uv_timer_t, heap_node); | ||
if (timer->due > loop->time) | ||
break; |
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.
style: 2 spaces.
Looks good! I'll run tests a bit later in the day. |
Thanks for your patient help. |
I'm going to hold this one, for some reason it seems to introduce a quite acute performance penalty, as measured by the million timer benchmark:
|
Yeah, I have found it, I will review the code carefully |
Thank you!
|
Maybe we should try RB trees on the linux branch? |
The heap was faster there, that's why we switched to it in the first place. |
Also, conceptually, a heap is the appropriate data structure to use here. I didn't dive into the details as of why this patch makes the code slower though. |
A heap is faster when the prevalent operation is looking up the first element (i.e. the min-heap operation) but it may be slower when there is a lot of mutation taking place. A heap has to walk up all the way to the root when a node is removed whereas a red-black tree usually only has to move up two or three nodes. The million-timers benchmark is kind of pathological in that it creates a large number of timers, then immediately expires and removes them again. I added it to make sure that worst case behavior with a heap is not significantly worse than with a red-black tree but it's not really representative of average real world usage. It does surprise me that performance is so bad on Windows, though. |
Oh, you're totally right, Ben! I'll reevaluate. |
Ben is exactly right! I have done a performance analysis, and the report below: pr/1476 19.88 seconds total Call Hierarchy: As we can see from the report, the test spends 79.89% time on v1.x 12.24 seconds total Call Hierarchy: the |
what did you use to test this? looks like a nice tool |
I was about to write a benchmark which tested a non-pathological usage of timers, to test this impact again, but when reading the benchmark this is what it does:
Is this really the pathological use case @bnoordhuis? We are actually leveing the timers expire. Well, each loop iteration we'll remove a thousand of them, but they are not readded because they are not repeating, so I'm a bit confused :-S |
It's pathological in that most activity is inserting/deleting timers whereas the common use case normally is looking up the first expiring timer. You can approximate the common use case by calling |
Oh, another thing: it's possible the heap performs worse due to cache eviction issues. Benchmark numbers on my machine were pretty good but it has obscene amounts of L1 and L2 data cache. YMMV. |
a new pr on v1.x, #1475