Skip to content

erts: Skip pollset update on scheduler-polled re-arm - #11525

Open
lpgauth wants to merge 1 commit into
erlang:masterfrom
lpgauth:sched-poll-nif-rearm
Open

erts: Skip pollset update on scheduler-polled re-arm#11525
lpgauth wants to merge 1 commit into
erlang:masterfrom
lpgauth:sched-poll-nif-rearm

Conversation

@lpgauth

@lpgauth lpgauth commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Since the scheduler pollset migration for enif_select (OTP 28), an fd
that keeps getting re-armed for reads by the same process is moved to
the scheduler pollset, where it stays registered for IN events and
re-arming is meant to be free. That never happens in practice: the
control path is entered on every re-arm and, while the scheduler
pollset part is already short-circuited, it still falls through to an
epoll_ctl(MOD) with an empty event set on the normal pollset. One
syscall per wakeup, changing nothing.

This skips the pollset update when re-arming a read select on an fd
that is enabled in the scheduler pollset. Nothing changes in either
pollset in that case: IN stays enabled in the scheduler pollset and
the normal pollset only carries the other events, which the call does
not touch. The trigger handler still clears IN from state->events, so
an fd that stops being re-armed migrates back to a poll thread as
before, and cancel/stop still go through the control path.

With a pipelined request/response socket NIF workload (64 callers,
16 connections), epoll_ctl calls drop from one per wakeup (~156k per
350k requests) to connection setup only (~240), worth ~2% end-to-end
throughput on 8 cores.

@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

CT Test Results

    3 files    136 suites   54m 0s ⏱️
1 693 tests 1 636 ✅ 57 💤 0 ❌
2 336 runs  2 261 ✅ 75 💤 0 ❌

Results for commit 6b57651.

♻️ This comment has been updated with latest results.

To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass.

See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally.

Artifacts

// Erlang/OTP Github Action Bot

An enif_select fd migrated to the scheduler pollset stays registered
for IN events across triggers, so a re-arming enif_select has nothing
to change in either pollset. The scheduler pollset control was
already short-circuited for this case, but the call still fell
through to an erts_poll_control on the normal pollset: one
epoll_ctl(MOD) per wakeup that changes nothing. Skip the pollset
update entirely when re-arming a read select on an fd that is
enabled in the scheduler pollset. The trigger handler still clears
IN from state->events, so fds that stop being re-armed migrate back
to a poll thread as before.

With a pipelined request/response socket NIF workload (64 callers,
16 connections), epoll_ctl calls drop from one per wakeup (~156k
per 350k requests) to connection setup only (~240), worth ~2%
end-to-end throughput on 8 cores.
@lpgauth
lpgauth force-pushed the sched-poll-nif-rearm branch from c3ef529 to 6b57651 Compare August 23, 2026 00:53
@jhogberg jhogberg self-assigned this Aug 24, 2026
@garazdawi

Copy link
Copy Markdown
Member

Nice find! The state machine in check io has become really complex... maybe it is time to do something about that. I should probably also spend some time to write some whitebox tests in order to catch these types of issues.

@IngelaAndin IngelaAndin added the team:VM Assigned to OTP team VM label Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

team:VM Assigned to OTP team VM

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants