-
Notifications
You must be signed in to change notification settings - Fork 996
Add goroutine core affinity support for RP2040/RP2350 systems #5092
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
Open
amken3d
wants to merge
7
commits into
tinygo-org:dev
Choose a base branch
from
amken3d:dev-rp2-core-pinning
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 2 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
16409a2
Add goroutine core affinity support for RP2040/RP2350 systems
amken3d 870af4f
Refactored based on Elias' comments
amken3d c00fc84
Improved LockCore and UnlockCore documentation for clarity on usage, …
amken3d 9025053
Add runtime.Gosched call in LockCore for improved scheduling
amken3d 8820b4e
Removed superfluous comments
amken3d 3ca2b71
Added GoSched() to scheduler_cores.go instead of incorrectly calling …
amken3d 217adfb
Remove unused import of "runtime" in machine_rp2_cores.go
amken3d File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| // This example demonstrates goroutine core pinning on multi-core systems (RP2040/RP2350). | ||
| // It shows how to pin goroutines to specific CPU cores and verify their execution. | ||
|
|
||
| //go:build rp2040 || rp2350 | ||
|
|
||
| package main | ||
|
|
||
| import ( | ||
| "machine" | ||
| "runtime" | ||
| "time" | ||
| ) | ||
|
|
||
| func main() { | ||
| time.Sleep(5 * time.Second) | ||
| println("=== Core Pinning Example ===") | ||
| println("Number of CPU cores:", runtime.NumCPU()) | ||
| println("[main] Main starting on core:", machine.CurrentCore()) | ||
| println() | ||
|
|
||
| // Example 1: Pin using standard Go API (LockOSThread) | ||
| // This pins to whichever core this goroutine is currently running on | ||
| runtime.LockOSThread() | ||
| println("[main] Pinned using runtime.LockOSThread()") | ||
| println("[main] Running on core:", machine.CurrentCore()) | ||
| runtime.UnlockOSThread() | ||
| println("[main] Unpinned using runtime.UnlockOSThread()") | ||
| println() | ||
|
|
||
| // Example 2: Pin to a specific core using machine package | ||
| machine.LockCore(0) | ||
| println("[main] Explicitly pinned to core 0 using machine.LockCore()") | ||
| println() | ||
|
|
||
| // Start a goroutine pinned to core 1 | ||
| go core1Worker() | ||
|
|
||
| // Start a goroutine using standard LockOSThread | ||
| go standardLockWorker() | ||
|
|
||
| // Start an unpinned goroutine (can run on either core) | ||
| go unpinnedWorker() | ||
|
|
||
| // Main loop on core 0 | ||
| for i := 0; i < 10; i++ { | ||
| println("[main] loop", i, "on CPU", machine.CurrentCore()) | ||
| time.Sleep(500 * time.Millisecond) | ||
| } | ||
|
|
||
| // Unpin and let main run on any core | ||
| machine.UnlockCore() | ||
| println() | ||
| println("[main] Unpinned using machine.UnlockCore()") | ||
|
|
||
| // Continue running for a bit to show potential migration | ||
| for i := 0; i < 5; i++ { | ||
| println("[main] unpinned loop on CPU", machine.CurrentCore()) | ||
| time.Sleep(500 * time.Millisecond) | ||
| } | ||
|
|
||
| println() | ||
| println("Example complete!") | ||
| } | ||
|
|
||
| // Worker function that pins to core 1 using explicit core selection | ||
| func core1Worker() { | ||
| // Pin this goroutine to core 1 explicitly | ||
| machine.LockCore(1) | ||
| println("[core1-worker] Worker pinned to core 1 using machine.LockCore()") | ||
|
|
||
| for i := 0; i < 10; i++ { | ||
| println("[core1-worker] loop", i, "on CPU", machine.CurrentCore()) | ||
| time.Sleep(500 * time.Millisecond) | ||
| } | ||
|
|
||
| println("[core1-worker] Finished") | ||
| } | ||
|
|
||
| // Worker function that uses standard Go LockOSThread() | ||
| func standardLockWorker() { | ||
| // Pin this goroutine to whichever core it starts on | ||
| runtime.LockOSThread() | ||
| defer runtime.UnlockOSThread() | ||
|
|
||
| core := machine.CurrentCore() | ||
| println("[std-lock-worker] Worker locked using runtime.LockOSThread()") | ||
| println("[std-lock-worker] Running on core:", core) | ||
|
|
||
| for i := 0; i < 10; i++ { | ||
| println("[std-lock-worker] loop", i, "on CPU", machine.CurrentCore()) | ||
| time.Sleep(600 * time.Millisecond) | ||
| } | ||
|
|
||
| println("[std-lock-worker] Finished") | ||
| } | ||
|
|
||
| // Worker function that is not pinned (can run on any core) | ||
| func unpinnedWorker() { | ||
| println("[unpinned-worker] Starting") | ||
|
|
||
| for i := 0; i < 10; i++ { | ||
| cpu := machine.CurrentCore() | ||
| println("[unpinned-worker] loop", i, "on CPU", cpu) | ||
| time.Sleep(700 * time.Millisecond) | ||
|
|
||
| // Yield to potentially migrate to another core | ||
| runtime.Gosched() | ||
| } | ||
|
|
||
| println("[unpinned-worker] Finished") | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| //go:build (rp2040 || rp2350) && scheduler.cores | ||
|
|
||
| package machine | ||
|
|
||
| const numCPU = 2 // RP2040 and RP2350 both have 2 cores | ||
|
|
||
| // LockCore implementation for the cores scheduler. | ||
| func LockCore(core int) { | ||
| if core < 0 || core >= numCPU { | ||
| panic("machine: core out of range") | ||
| } | ||
| machineLockCore(core) | ||
| } | ||
|
|
||
| // UnlockCore implementation for the cores scheduler. | ||
| func UnlockCore() { | ||
| machineUnlockCore() | ||
| } | ||
|
|
||
| // Internal functions implemented in runtime/scheduler_cores.go | ||
| // | ||
| //go:linkname machineLockCore runtime.machineLockCore | ||
| func machineLockCore(core int) | ||
|
|
||
| //go:linkname machineUnlockCore runtime.machineUnlockCore | ||
| func machineUnlockCore() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| //go:build (rp2040 || rp2350) && !scheduler.cores | ||
|
|
||
| package machine | ||
|
|
||
| // LockCore is not available without the cores scheduler. | ||
| // This is a stub that panics. | ||
| func LockCore(core int) { | ||
| panic("machine.LockCore: not available without scheduler.cores") | ||
| } | ||
|
|
||
| // UnlockCore is not available without the cores scheduler. | ||
| // This is a stub that panics. | ||
| func UnlockCore() { | ||
| panic("machine.UnlockCore: not available without scheduler.cores") | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -98,14 +98,18 @@ func os_sigpipe() { | |
| } | ||
|
|
||
| // LockOSThread wires the calling goroutine to its current operating system thread. | ||
| // Stub for now | ||
| // On microcontrollers with multiple cores (e.g., RP2040/RP2350), this pins the | ||
| // goroutine to the core it's currently running on. | ||
|
Comment on lines
+101
to
+102
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it more precise to say "with the "cores" scheduler"? |
||
| // Called by go1.18 standard library on windows, see https://github.com/golang/go/issues/49320 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While here, remove this now irrelevant comment. |
||
| func LockOSThread() { | ||
| lockOSThreadImpl() | ||
| } | ||
|
|
||
| // UnlockOSThread undoes an earlier call to LockOSThread. | ||
| // Stub for now | ||
| // On microcontrollers with multiple cores, this unpins the goroutine, allowing | ||
| // it to run on any available core. | ||
| func UnlockOSThread() { | ||
| unlockOSThreadImpl() | ||
| } | ||
|
|
||
| // KeepAlive makes sure the value in the interface is alive until at least the | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This needs a more detailed description. For example, it doesn't say what happens if the target core is busy. I believe
LockCorereturns. If so, this is surprising to me; I would expect that onceLockCorereturns, the calling goroutine is running on the target core.