Skip to content

perf: add initial threadboost mechanism (for ohos). - #46843

Merged
jschwe merged 5 commits into
servo:mainfrom
jschwe:thread_affinity
Aug 7, 2026
Merged

jschwe merged 5 commits into
servo:mainfrom
jschwe:thread_affinity

Conversation

@jschwe

@jschwe jschwe commented Jul 28, 2026 •

Copy link
Copy Markdown
Member

The abstraction design will need to be iterated on, especially since the implementation might diverge strongly based on the target platfrom. On non heterogeneous platforms, we probably don't need to do anything, since all cores are equal.

Background: On a harmonyos device the spread-out nature of servo, with threads calling out to other threads, doesn't interact well with the default scheduler, since the threads on the critical path might not be busy enough (consecutively) to be boosted to a medium or large core. Since the little cores on this device are ~1/3-1/4 as capable as the medium/large ones (according to the sysfs capability metric) this can have a large impact on performance.

Improvements on ohos:

This PR should be seen as a first step towards #46813. The abstraction itself as is, is not great, but partially also can't be avoided since mechanisms will diverge depending on the platform. Part of the reason this PR works as well as it does, despite only explicitly prioritising 3 threads, is that all child threads spawned after the thread affinity mask takes effect inherit the affinity mask (which is an implementation detail of the ohos platform, since other platforms likely would not want to use affinity masks). The thread priority set via OH_QoS_SetThreadQoS only affects the current thread and not any children.

Note: I also checked, and notably chromium on ohos doesn't use thread affinity masks. However, it does more work on the main thread, which seems to interact better with the scheduler heuristics since it is boosted to medium cores under load (or perhaps the scheduler is aware of important chromium threads, since it is an important system app).

Testing: We currently don't measure production performance before merging to main.

The actual design will need to be iterated on, especially since
the implementation might diverge strongly based on the target platfrom.
On non heterogeneous platforms we probably don't need to do anything,
since all cores are equal. On a harmonyos device the spread-out nature
of servo, with threads calling out to other threads, doesn't interact
well with the default scheduler, since the threads on the critical path
might not be busy enough (consecutively) to be boosted to a medium or
large core. Since the little cores on this device are ~1/3 as capable
as the large ones (according to the sysfs capability metric) this can
have a large impact on performance.
With all threads boosted to medium cores, we get a moderate 8% reduction
in LCP on wikipedia.
For the reproducer in servo#45199 it is very noticable and boosts the
framerate to 60 fps, where it is capped. With active touch input
which raises the display frequency to 120hz, we reach ~74fps.

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
@jschwe
jschwe requested a review from gterzian as a code owner July 28, 2026 20:31
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Jul 28, 2026
@jschwe jschwe added the T-ohos Do a try run on OpenHarmony label Jul 28, 2026
@github-actions github-actions Bot removed the T-ohos Do a try run on OpenHarmony label Jul 28, 2026
@github-actions

Copy link
Copy Markdown

🔨 Triggering try run (#30396696567) for OpenHarmony

@github-actions

Copy link
Copy Markdown

🐰 Bencher Report

ProjectServo
Branch46843/PR
TestbedHUAWEI Mate 60 Pro

⚠️ WARNING: Truncated view!

The full continuous benchmarking report exceeds the maximum length allowed on this platform.

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

🐰 View full continuous benchmarking report in Bencher

@github-actions

Copy link
Copy Markdown

✨ Try run (#30396696567) succeeded.

@yezhizhen
yezhizhen self-requested a review July 29, 2026 03:03
Comment thread ports/servoshell/egl/ohos/mod.rs Outdated

// After init_app so the thread affinity mask doesn't affect all children spawned in init_app.
servo_base::threadboost::mark_thread_as_critical();

@mrobinson mrobinson Jul 29, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not be exposing any new public API from crates that are not servo. Couldn't this happen internally in Servo when the Servo instance is created?

@jschwe jschwe Jul 29, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not sure. I'd be wary of just modifying the embedder thread from in servo without the embedder opting in. I think we'd need to have the full discussion about how the final design abstraction should look like first (which probably includes experimenting first on multiple different heterogeneous targets (android, mobile Linux, mobile windows?) to figure out what works and what is required.

Would you be fine with just re-exporting this from servo for now?

Edit: oh and the M macs also have effeciency cores - would probably also be interesting to check on what cores servo is scheduled there.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this could be a preference like set_thread_affinities. I think a preference is nicer for embedder rather than knowing that they have to call a function to make Servo go fast. Then we can implement this for other platforms.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this could be a preference like set_thread_affinities

I would prefer to defer a public API like this until we have done experimentation with what works / doesn't work on other platforms, since it will likely change the shape of this API (right now it's basically "this works on ohos"), but we should try and see what works on android, arm windows, arm mac and mobile linux.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand. The new mark_thread_as_critical API is also public.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm considering marking the re-export as #[doc(hidden)] for now.

I think a preference is nicer for embedder rather than knowing that they have to call a function to make Servo go fast

I think a serious embedder should be aware of this, since they should also be able to customize the policy (of what exactly happens for critical threads). Also worth noting that by pinning most servo threads to larger cores, the embedder thread also is more likely to scheduled there, since the scheduler will prefer free hot cores I believe.

It may be that an embedder does not want the main thread to have custom thread affinities, but does want servos thread to prefer larger cores.
My rough idea would be to add a section to "Embedding Servo" in the servo book explaining the mechanism (and trade-offs), after we come up with a generalization.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this could be a preference like set_thread_affinities

I would prefer to defer a public API like this until we have done experimentation with what works / doesn't work on other platforms, since it will likely change the shape of this API (right now it's basically "this works on ohos"), but we should try and see what works on android, arm windows, arm mac and mobile linux.

We could also expose this as a preference now and mark the preference with #[doc(hidden)].

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay - I added a pref to toggle the feature and moved the embedder thread boost to servo initialization.
I'm planning to iterate on this and probably the embedder thread boost will disappear again, since it seems like the important threads are all child threads (from webrender), which could / should be boosted individually.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrobinson is your concern resolved?

@servo-highfive servo-highfive added S-needs-code-changes Changes have not yet been made that were requested by a reviewer. S-awaiting-review There is new code that needs to be reviewed. and removed S-awaiting-review There is new code that needs to be reviewed. S-needs-code-changes Changes have not yet been made that were requested by a reviewer. labels Jul 29, 2026
@webbeef

webbeef commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

I tested a quick implementation for Linux based on the gdt_cpus crate and I got similar improvements on speedometer. My Macbook Air M1 running Fedora (4 performance cores and 4 efficiency cores) moves from a score of 4.20 to 7.71

So I would say the sooner we get that change in, the better :)

@jschwe
jschwe requested a review from mrobinson July 31, 2026 18:53
Comment thread components/shared/base/threadboost.rs Outdated
@Narfinger

Copy link
Copy Markdown
Contributor

Can confirm that for most cases speedometer on harmonyos shows improvement. So I think this should be merged.

Comment thread components/constellation/constellation.rs
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
jschwe added 2 commits August 5, 2026 07:11
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>

@mrobinson mrobinson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good with a few nits:

Comment thread components/shared/base/threadboost.rs Outdated
Comment thread components/shared/base/threadboost.rs Outdated
Comment thread components/shared/base/threadboost.rs Outdated
Comment thread components/shared/base/threadboost.rs Outdated
Comment thread components/shared/base/threadboost.rs Outdated
Comment thread components/shared/base/threadboost.rs Outdated
Comment thread components/shared/base/threadboost.rs Outdated
Comment thread components/shared/base/threadboost.rs Outdated
@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Aug 6, 2026
Co-authored-by: Martin Robinson <martin@abandonedwig.info>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Aug 6, 2026
@jschwe
jschwe enabled auto-merge August 7, 2026 04:45
@jschwe
jschwe added this pull request to the merge queue Aug 7, 2026
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Aug 7, 2026
Merged via the queue into servo:main with commit 290a778 Aug 7, 2026
58 of 60 checks passed
@jschwe
jschwe deleted the thread_affinity branch August 7, 2026 06:40
@servo-highfive servo-highfive removed the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Aug 7, 2026
pull Bot pushed a commit to Haofei/servo that referenced this pull request Aug 11, 2026
servo#47027)

Follow-up to servo#46843.

Instead of boosting the embedder thread, which happened to also boost
the webrender (children) threads (threads spawned after we changed the
thread affinity for the embedder thread), boost the webrender threads
explicitly and remove the embedder thread boost. This seems to have the
same net performance, hence it's better to avoid changing the properties
of the embedder thread.
Additionally modified the boosting API, so that we can specify a
priority level, and opt-in to the thread affinity boosting. This gives
us more flexibility, since some threads might just want one or the
other.
In the future this might also be useful if we want to de-prioritize
certain threads.

Testing: We don't have any automated tests measuring performance or
thread placement.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
RichardTjokroutomo pushed a commit to RichardTjokroutomo/servo that referenced this pull request Oct 6, 2026
The abstraction design will need to be iterated on, especially since the
implementation might diverge strongly based on the target platfrom. On
non heterogeneous platforms, we probably don't need to do anything,
since all cores are equal.

Background: On a harmonyos device the spread-out nature of servo, with
threads calling out to other threads, doesn't interact well with the
default scheduler, since the threads on the critical path might not be
busy enough (consecutively) to be boosted to a medium or large core.
Since the little cores on this device are ~1/3-1/4 as capable as the
medium/large ones (according to the sysfs capability metric) this can
have a large impact on performance.

Improvements on ohos:  
- a moderate 8% reduction in LCP on wikipedia.
- For the reproducer in servo#45199 it is very noticable and boosts the
framerate to 60 fps, where it is capped. With active touch input which
raises the display frequency to 120hz, we reach ~74fps.
In speedometer on my test-device the test score improves by ~50%. 

This PR should be seen as a first step towards servo#46813. The abstraction
itself as is, is not great, but partially also can't be avoided since
mechanisms will diverge depending on the platform. Part of the reason
this PR works as well as it does, despite only explicitly prioritising 3
threads, is that all child threads spawned after the thread affinity
mask takes effect inherit the affinity mask (which is an implementation
detail of the ohos platform, since other platforms likely would not want
to use affinity masks). The thread priority set via
`OH_QoS_SetThreadQoS` only affects the current thread and not any
children.

Note: I also checked, and notably chromium on ohos doesn't use thread
affinity masks. However, it does more work on the main thread, which
seems to interact better with the scheduler heuristics since it is
boosted to medium cores under load (or perhaps the scheduler is aware of
important chromium threads, since it is an important system app).

Testing: We currently don't measure production performance before
merging to main.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Co-authored-by: Martin Robinson <martin@abandonedwig.info>
RichardTjokroutomo pushed a commit to RichardTjokroutomo/servo that referenced this pull request Oct 6, 2026
servo#47027)

Follow-up to servo#46843.

Instead of boosting the embedder thread, which happened to also boost
the webrender (children) threads (threads spawned after we changed the
thread affinity for the embedder thread), boost the webrender threads
explicitly and remove the embedder thread boost. This seems to have the
same net performance, hence it's better to avoid changing the properties
of the embedder thread.
Additionally modified the boosting API, so that we can specify a
priority level, and opt-in to the thread affinity boosting. This gives
us more flexibility, since some threads might just want one or the
other.
In the future this might also be useful if we want to de-prioritize
certain threads.

Testing: We don't have any automated tests measuring performance or
thread placement.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-awaiting-review There is new code that needs to be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants