|
1 | 1 | // This example demonstrates goroutine core pinning on multi-core systems (RP2040/RP2350). |
2 | 2 | // It shows how to pin goroutines to specific CPU cores and verify their execution. |
3 | | -// |
4 | 3 |
|
5 | 4 | //go:build rp2040 || rp2350 |
6 | 5 |
|
7 | 6 | package main |
8 | 7 |
|
9 | 8 | import ( |
| 9 | + "machine" |
10 | 10 | "runtime" |
11 | 11 | "time" |
12 | 12 | ) |
13 | 13 |
|
14 | 14 | func main() { |
| 15 | + time.Sleep(5 * time.Second) |
15 | 16 | println("=== Core Pinning Example ===") |
16 | 17 | println("Number of CPU cores:", runtime.NumCPU()) |
17 | | - println("Main starting on core:", runtime.CurrentCPU()) |
| 18 | + println("[main] Main starting on core:", machine.CurrentCore()) |
18 | 19 | println() |
19 | 20 |
|
20 | | - // Pin main goroutine to core 0 |
21 | | - runtime.LockToCore(0) |
22 | | - println("Main pinned to core:", runtime.GetAffinity()) |
| 21 | + // Example 1: Pin using standard Go API (LockOSThread) |
| 22 | + // This pins to whichever core this goroutine is currently running on |
| 23 | + runtime.LockOSThread() |
| 24 | + println("[main] Pinned using runtime.LockOSThread()") |
| 25 | + println("[main] Running on core:", machine.CurrentCore()) |
| 26 | + runtime.UnlockOSThread() |
| 27 | + println("[main] Unpinned using runtime.UnlockOSThread()") |
| 28 | + println() |
| 29 | + |
| 30 | + // Example 2: Pin to a specific core using machine package |
| 31 | + machine.LockCore(0) |
| 32 | + println("[main] Explicitly pinned to core 0 using machine.LockCore()") |
23 | 33 | println() |
24 | 34 |
|
25 | 35 | // Start a goroutine pinned to core 1 |
26 | 36 | go core1Worker() |
27 | 37 |
|
| 38 | + // Start a goroutine using standard LockOSThread |
| 39 | + go standardLockWorker() |
| 40 | + |
28 | 41 | // Start an unpinned goroutine (can run on either core) |
29 | 42 | go unpinnedWorker() |
30 | 43 |
|
31 | 44 | // Main loop on core 0 |
32 | 45 | for i := 0; i < 10; i++ { |
33 | | - println("Core 0 (main):", i, "on CPU", runtime.CurrentCPU()) |
| 46 | + println("[main] loop", i, "on CPU", machine.CurrentCore()) |
34 | 47 | time.Sleep(500 * time.Millisecond) |
35 | 48 | } |
36 | 49 |
|
37 | 50 | // Unpin and let main run on any core |
38 | | - runtime.UnlockFromCore() |
| 51 | + machine.UnlockCore() |
39 | 52 | println() |
40 | | - println("Main unpinned, affinity:", runtime.GetAffinity()) |
| 53 | + println("[main] Unpinned using machine.UnlockCore()") |
41 | 54 |
|
42 | | - // Continue running for a bit to show migration |
| 55 | + // Continue running for a bit to show potential migration |
43 | 56 | for i := 0; i < 5; i++ { |
44 | | - println("Unpinned main on CPU", runtime.CurrentCPU()) |
| 57 | + println("[main] unpinned loop on CPU", machine.CurrentCore()) |
45 | 58 | time.Sleep(500 * time.Millisecond) |
46 | 59 | } |
47 | 60 |
|
48 | 61 | println() |
49 | 62 | println("Example complete!") |
50 | 63 | } |
51 | 64 |
|
52 | | -// Worker function that runs on core 1 |
| 65 | +// Worker function that pins to core 1 using explicit core selection |
53 | 66 | func core1Worker() { |
54 | | - // Pin this goroutine to core 1 |
55 | | - runtime.LockToCore(1) |
56 | | - println("Worker pinned to core:", runtime.GetAffinity()) |
| 67 | + // Pin this goroutine to core 1 explicitly |
| 68 | + machine.LockCore(1) |
| 69 | + println("[core1-worker] Worker pinned to core 1 using machine.LockCore()") |
57 | 70 |
|
58 | 71 | for i := 0; i < 10; i++ { |
59 | | - println(" Core 1 (worker):", i, "on CPU", runtime.CurrentCPU()) |
| 72 | + println("[core1-worker] loop", i, "on CPU", machine.CurrentCore()) |
60 | 73 | time.Sleep(500 * time.Millisecond) |
61 | 74 | } |
62 | 75 |
|
63 | | - println(" Core 1 worker finished") |
| 76 | + println("[core1-worker] Finished") |
| 77 | +} |
| 78 | + |
| 79 | +// Worker function that uses standard Go LockOSThread() |
| 80 | +func standardLockWorker() { |
| 81 | + // Pin this goroutine to whichever core it starts on |
| 82 | + runtime.LockOSThread() |
| 83 | + defer runtime.UnlockOSThread() |
| 84 | + |
| 85 | + core := machine.CurrentCore() |
| 86 | + println("[std-lock-worker] Worker locked using runtime.LockOSThread()") |
| 87 | + println("[std-lock-worker] Running on core:", core) |
| 88 | + |
| 89 | + for i := 0; i < 10; i++ { |
| 90 | + println("[std-lock-worker] loop", i, "on CPU", machine.CurrentCore()) |
| 91 | + time.Sleep(600 * time.Millisecond) |
| 92 | + } |
| 93 | + |
| 94 | + println("[std-lock-worker] Finished") |
64 | 95 | } |
65 | 96 |
|
66 | 97 | // Worker function that is not pinned (can run on any core) |
67 | 98 | func unpinnedWorker() { |
68 | | - println("Unpinned worker starting, affinity:", runtime.GetAffinity()) |
| 99 | + println("[unpinned-worker] Starting") |
69 | 100 |
|
70 | 101 | for i := 0; i < 10; i++ { |
71 | | - cpu := runtime.CurrentCPU() |
72 | | - println(" Unpinned worker:", i, "on CPU", cpu) |
| 102 | + cpu := machine.CurrentCore() |
| 103 | + println("[unpinned-worker] loop", i, "on CPU", cpu) |
73 | 104 | time.Sleep(700 * time.Millisecond) |
74 | 105 |
|
75 | 106 | // Yield to potentially migrate to another core |
76 | 107 | runtime.Gosched() |
77 | 108 | } |
78 | 109 |
|
79 | | - println(" Unpinned worker finished") |
| 110 | + println("[unpinned-worker] Finished") |
80 | 111 | } |
0 commit comments