Closed Bug 1787974 Opened 2 years ago Closed 2 years ago

Rework TImerThread's timer storage to allow in-order access

Categories

(Core :: XPCOM, task)

task

Tracking

()

RESOLVED FIXED
111 Branch
Tracking Status
firefox111 --- fixed

People

(Reporter: mozbugz, Assigned: jlink)

References

Details

Attachments

(16 files, 13 obsolete files)

48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review

TimerThread::mTimers is currently an array of unique pointers to Entry's, managed as a binary heap (with std::push_heap and std::pop_heap, such that the first element always has the earliest timeout).
This arrangement was implemented in bug 1325254, and makes adding timers very fast, but makes it difficult to look at multiple timers in order, which will be needed when trying to coalesce timers in bug 1783405.

This bug intends to change mTimers into a sorted array of entries.
I will attach the current WIP soon, but I won't have the time to finish it together with bug 1783405 (it doesn't make much sense to do one without the other.)

Testing it showed that the overall speed was pretty much the same.
While adding elements in a sorted array is intuitively slower than using std::push_heap, this was effectively balanced by a number of factors:

  • The list is generally fairly small, tens to around 100 elements at times, so linear access is fast on modern CPUs thanks to cache locality and prefetching.
  • Storing entries instead of pointers to on-memory-heap entries, saving lots of (de)allocations and indirections.
  • Removing timers is done by nulling a pointer in entries, and when adding we can reuse these "empty" entries (either directly, or when inserting and therefore moving elements to the right until the first empty entry), resulting in few element moves in practice.

This adds more information in the "AddTimer" markers, about the internal state
of the TimerThread when adding each timer.

Also both AddTimer and RemoveTimer markers now show the duration of these
function calls.

Depends on D155932

Note that it's about 4 times slower in practice! But this will be balanced in later patches.

Depends on D155935

nsTimerImplHolder can now be removed.

Depends on D155936

This removes memory heap allocations and deallocations, and indirections to access entries.

Depends on D155937

So entries now store the given timeout value (instead of clamping it to after "now").
And we store this timeout first, as it's accessed more frequently.

Depends on D155938

Inserting elements in the sorted array is more costly than pushing to a binary
heap, but this is balanced by the removal of indirections and de/allocations,
and the upcoming re-use of empty entries.

In the end, this change is necessary to allow future improvements like timer-
coalescing, where we'll need to see multiple timers in order.

Bonus: FindNextFireTimeForCurrentThread changes a lot, because it used to have
to pop_heap elements and then push_heap them back! (Sometimes resulting in
subtle changes for timers with the same timeout.) So this one benefits the
most.

Depends on D155941

This makes the code more complex, but it is needed to keep this most-used
function efficient related to its old implementation using std::push_heap.

Depends on D155942

This adds some valuable markers showing the times from adding to dispatching a
timer, and then from dispatching to actually running the timer code.

Depends on D155943

Assignee: nobody → mozbugz
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true

Time is almost up for me, so I won't have time to progress this further myself. Happy to answer any questions though.
Good continuation, I hope this proves to be useful.

Assignee: mozbugz → nobody
Status: ASSIGNED → NEW
Assignee: nobody → jlink

This adds more information in the "AddTimer" markers, about the internal state
of the TimerThread when adding each timer.

Also both AddTimer and RemoveTimer markers now show the duration of these
function calls.

Depends on D164281

Note that it's about 4 times slower in practice! But this will be balanced in later patches.

Depends on D164284

nsTimerImplHolder can now be removed.

Depends on D164285

This removes memory heap allocations and deallocations, and indirections to access entries.

Depends on D164286

So entries now store the given timeout value (instead of clamping it to after "now").
And we store this timeout first, as it's accessed more frequently.

Depends on D164287

Inserting elements in the sorted array is more costly than pushing to a binary
heap, but this is balanced by the removal of indirections and de/allocations,
and the upcoming re-use of empty entries.

In the end, this change is necessary to allow future improvements like timer-
coalescing, where we'll need to see multiple timers in order.

Bonus: FindNextFireTimeForCurrentThread changes a lot, because it used to have
to pop_heap elements and then push_heap them back! (Sometimes resulting in
subtle changes for timers with the same timeout.) So this one benefits the
most.

Depends on D164290

This makes the code more complex, but it is needed to keep this most-used
function efficient related to its old implementation using std::push_heap.

Depends on D164291

This adds some valuable markers showing the times from adding to dispatching a
timer, and then from dispatching to actually running the timer code.

Depends on D164292

Attachment #9292186 - Attachment is obsolete: true
Attachment #9292187 - Attachment is obsolete: true
Attachment #9292188 - Attachment is obsolete: true
Attachment #9292189 - Attachment is obsolete: true
Attachment #9292190 - Attachment is obsolete: true
Attachment #9292191 - Attachment is obsolete: true
Attachment #9292192 - Attachment is obsolete: true
Attachment #9292193 - Attachment is obsolete: true
Attachment #9292194 - Attachment is obsolete: true
Attachment #9292195 - Attachment is obsolete: true
Attachment #9292196 - Attachment is obsolete: true
Attachment #9292197 - Attachment is obsolete: true
Attachment #9292198 - Attachment is obsolete: true
Attachment #9307445 - Attachment description: WIP: Bug 1787974 - ***OPTIONAL PATCH*** Add waiting/added-first/notified to marker in TimerThread::AddTimer → Bug 1787974 - Add waiting/added-first/notified to marker in TimerThread::AddTimer r=florian,smaug
Attachment #9307447 - Attachment description: WIP: Bug 1787974 - ***OPTIONAL PATCH*** Better marker for TimerThread::RemoveTimer - → Bug 1787974 - Better marker for TimerThread::RemoveTimer r=florian,smaug
Attachment #9307448 - Attachment description: WIP: Bug 1787974 - Only nsTimerImplHolder::Forget needs to be public - → Bug 1787974 - Only nsTimerImplHolder::Forget needs to be public r=florian,smaug
Attachment #9307449 - Attachment description: WIP: Bug 1787974 - Style: Put Entry private section at the bottom of the class - → Bug 1787974 - Style: Put Entry private section at the bottom of the class r=florian,smaug
Attachment #9307450 - Attachment description: WIP: Bug 1787974 - Do a linear search in RemoveTimerInternal to stop going through the holder - → Bug 1787974 - Do a linear search in RemoveTimerInternal to stop going through the holder r=florian,smaug
Attachment #9307451 - Attachment description: WIP: Bug 1787974 - nsTimerImpl directly keeps track of whether it's in a TimerThread::Entry - → Bug 1787974 - nsTimerImpl directly keeps track of whether it's in a TimerThread::Entry r=florian,smaug
Attachment #9307452 - Attachment description: WIP: Bug 1787974 - TimerThread stores Entry's instead of UniquePtrs to Entry's - → Bug 1787974 - TimerThread stores Entry's instead of UniquePtrs to Entry's r=florian,smaug
Attachment #9307453 - Attachment description: WIP: Bug 1787974 - Entry constructor only takes an nsTimerImpl* - → Bug 1787974 - Entry constructor only takes an nsTimerImpl* r=florian,smaug
Attachment #9307454 - Attachment description: WIP: Bug 1787974 - Entry::Forget doesn't need a parameter - → Bug 1787974 - Entry::Forget doesn't need a parameter r=florian,smaug
Attachment #9307455 - Attachment description: WIP: Bug 1787974 - In TimerThread::Shutdown, only Take non-null entries - → Bug 1787974 - In TimerThread::Shutdown, only Take non-null entries r=florian,smaug
Attachment #9307456 - Attachment description: WIP: Bug 1787974 - Changing mTimers from heap to timestamp-sorted array - → Bug 1787974 - Changing mTimers from heap to timestamp-sorted array r=florian,smaug
Attachment #9307457 - Attachment description: WIP: Bug 1787974 - AddTimerInternal efficiently inserts by shifting entries until next gap - → Bug 1787974 - AddTimerInternal efficiently inserts by shifting entries until next gap r=florian,smaug
Attachment #9307458 - Attachment description: WIP: Bug 1787974 - ***OPTIONAL PATCH*** Markers timer added->removed and removed->run - → Bug 1787974 - Markers timer added->removed and removed->run r=florian,smaug
Attachment #9307459 - Attachment description: WIP: Bug 1787974 - Fixed problem where AddTimerInternal() could add a new timer in the wrong spot. → Bug 1787974 - Fixed problem where AddTimerInternal() could add a new timer in the wrong spot. r=florian,smaug

I also took a stab at documenting the behavior since I found it a bit
difficult to follow.

Depends on D164294

Attachment #9309091 - Attachment description: WIP: Bug 1787974 - Fixed unintended behavior change in TimerThread::FindNextFireTimeForCurrentThread that was causing some unit tests to fail. → Bug 1787974 - Fixed small logic change in FindNextFireTimeForCurrentThread(). r=smaug

Due to the possible presence of canceled timers at the "front" of the
list, mTimers[0] won't necessarily hold the next timer to be fired
unless we've cleaned up those front timers recently.

Attachment #9307445 - Attachment description: Bug 1787974 - Add waiting/added-first/notified to marker in TimerThread::AddTimer r=florian,smaug → WIP: Bug 1787974 - Add waiting/added-first/notified to marker in TimerThread::AddTimer
Attachment #9307447 - Attachment description: Bug 1787974 - Better marker for TimerThread::RemoveTimer r=florian,smaug → WIP: Bug 1787974 - Better marker for TimerThread::RemoveTimer
Attachment #9307458 - Attachment description: Bug 1787974 - Markers timer added->removed and removed->run r=florian,smaug → WIP: Bug 1787974 - Markers timer added->removed and removed->run

Olli, Florian: I've moved the three changes with the "top" of the stack and made sure that each change in the new stack compiles (with its dependent changes). I've also now disconnected the profiling changes from the rest of the stack as well.

It looks like all of the non-profiling changes have been approved so I'll probably try to land this a little later today unless you let me know and want to take one last look first. (I think I still have a few code review comments to respond to/close also.)

Pushed by opettay@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/e22855795b9f Only nsTimerImplHolder::Forget needs to be public r=smaug https://hg.mozilla.org/integration/autoland/rev/aad44174c4bf Style: Put Entry private section at the bottom of the class r=smaug https://hg.mozilla.org/integration/autoland/rev/5d58bb968292 Do a linear search in RemoveTimerInternal to stop going through the holder r=smaug https://hg.mozilla.org/integration/autoland/rev/b70b44f7828f nsTimerImpl directly keeps track of whether it's in a TimerThread::Entry r=smaug https://hg.mozilla.org/integration/autoland/rev/248126e639c3 TimerThread stores Entry's instead of UniquePtrs to Entry's r=smaug https://hg.mozilla.org/integration/autoland/rev/cc9b47f124a8 Entry constructor only takes an nsTimerImpl* r=smaug https://hg.mozilla.org/integration/autoland/rev/6603be788d60 Entry::Forget doesn't need a parameter r=smaug https://hg.mozilla.org/integration/autoland/rev/4c8d9ee64409 In TimerThread::Shutdown, only Take non-null entries r=smaug https://hg.mozilla.org/integration/autoland/rev/115dff277414 Changing mTimers from heap to timestamp-sorted array r=smaug https://hg.mozilla.org/integration/autoland/rev/facb9344bb5d AddTimerInternal efficiently inserts by shifting entries until next gap r=smaug https://hg.mozilla.org/integration/autoland/rev/f1f071954863 Fixed problem where AddTimerInternal() could add a new timer in the wrong spot. r=smaug https://hg.mozilla.org/integration/autoland/rev/199888078547 Fixed small logic change in FindNextFireTimeForCurrentThread(). r=smaug https://hg.mozilla.org/integration/autoland/rev/3e5c9fa28ebb Remove leading canceled timers before deciding if we added a new front timer r=smaug
Regressions: 1812080
See Also: → 1817034
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: