Skip to content

fix(runtime): avoid reentrant weak cleanup deadlocks - #2575

Open
cpunion wants to merge 3 commits into
xgo-dev:mainfrom
cpunion:codex/fix-weak-cleanup-reentrancy-20260912
Open

fix(runtime): avoid reentrant weak cleanup deadlocks#2575
cpunion wants to merge 3 commits into
xgo-dev:mainfrom
cpunion:codex/fix-weak-cleanup-reentrancy-20260912

Conversation

@cpunion

@cpunion cpunion commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

Problem

Fix a real weak-cleanup self-deadlock captured in the Qiniu Linux amd64 / LLVM 22 / Go 1.27 shard-0 investigation for #2567. In the failing diagnostic job, crypto/ecdsa was the only unfinished package. Native stacks taken two minutes apart show the same thread holding weakState.mu inside a weak cleanup, allocating during mapaccess1_fast64, recursively entering GC_invoke_finalizers, and then blocking in another weak cleanup on that same non-recursive mutex. The parent LLGo process is waiting for the test child, not looping in compilation. This weak callback code predates #2567 and is unchanged between its previously passing and subsequently timed-out revisions. The earlier timeout attempts have no surviving native stacks, so this PR does not claim each had the same cause.

Change

Weak cleanup now only atomically marks its handle dead and publishes it to an intrusive pending list. Registration detaches one batch and removes matching map entries while holding the existing registry lock. Each producer initializes its private node's ordinary next link before the sequentially consistent head CAS publishes it, stops accessing the node after publication, and the head Swap transfers the detached batch to the sole drainer; the head atomics therefore order the ordinary link accesses without redundant per-link atomics. A live-state check handles the interval between invalidation and publication; an identity check prevents a delayed old cleanup from deleting a new handle after address reuse. The pending list keeps handles reachable, but retains only the existing encoded referent identity. Weak-pointer reads are unchanged, and no background goroutine or collector policy change is introduced. Clearing each detached node's next prevents a user-held dead weak pointer from retaining the rest of the batch. Reclamation is registration-gated, with the explicit limits below.

Add regular weak-identity coverage and the opt-in test/_stress/runtime/weak regression. It uses only public weak.Make and runtime.GC APIs, keeps referents alive until a whole batch is registered, releases them from an exiting goroutine, and checks bounded collection progress. A helper-process deadline turns the old runtime deadlock into a finite test failure. There are no production test hooks, retries, sleeps, or reduced-pressure success conditions.

Standard-library reuse and reclamation limits

LLGo already compiles GOROOT's public weak.Pointer, Make, and Value implementation. This file provides the two collector-dependent runtime hooks. In Go 1.27's runtime, a registration node is attached to the referent's GC span; sweep removes that node, zeros the heap-allocated handle, and returns the node to the runtime allocator. A user-held weak pointer still retains the dead handle to preserve identity; the handle itself becomes collectible when no roots retain it. Those runtime routines depend on Go's span metadata, marking/sweeping synchronization, write barriers, and scheduler, so they cannot be directly reused with LLGo's BDWGC heap.

This PR removes dead registry entries only during the next non-nil weak.Make. If registration stops, the last dead batch and its map entries remain retained; neither Pointer.Value nor runtime.GC directly drains them. They retain handles, not the referents. The batch has no fixed size bound, so this is a real metadata-retention tradeoff, not a guarantee of bounded memory relative to currently live weak pointers. Continued registrations detach pending batches instead of accumulating all historical dead handles. Deleting entries also does not shrink the map's backing capacity or guarantee an immediate RSS reduction.

Each non-nil registration adds one atomic head exchange, including existing-handle lookups. A captured batch of D handles requires O(D) work under the registry mutex: taking one batch prevents cleanup arrivals from indefinitely extending that batch, but does not cap a single registration's latency. Each handle gains one pointer; the reduced closure allocation described below must not be interpreted as a whole-process memory saving. Pointer.Value keeps its existing allocation-free, lock-free path.

Complete reclamation after automatic GC with no subsequent registration needs a separately scheduled safe consumer or deeper collector integration. Adding a drain only to explicit runtime.GC would not cover automatic collection and would still require reentrancy analysis. A consumer must use an allocation-safe notification path; an ordinary channel is not automatically suitable because LLGo's channel receive can allocate while holding its own lock. Native LLGo goroutines currently use OS threads, so a dedicated worker also adds thread/stack and lifecycle costs. This PR does not add that mechanism or claim to solve idle metadata retention. The source comments document the ownership handoff, retained-identity distinction, and reclamation limits.

Local verification

Same stress test and default parameters, macOS arm64 Result
Unmodified main b07cd12ad, 3 independent executions 3/3 fail at the first batch with the helper's 60-second deadline; native samples reproduce the CI weak-callback/map-allocation/reentrant-lock stack
Fixed runtime, default GC marker configuration 53/53 pass, approximately 0.12 seconds per execution
Fixed runtime, GC_MARKERS=1 20/20 pass
Official Go, -race -count=10 Pass

llgo test -count=3 ./test/std/weak, go test -race -count=3 ./test/std/weak, and the selected wasm/atomic source-patch type-check tests also pass. Native disassembly confirms that the valid-handle cleanup path contains only loads/stores and atomic operations, with no lock, map, or allocator calls. Although a handle gains one pointer, the cleanup closure no longer captures a separate key: this arm64 build reduces direct registration allocation requests from four allocations totaling 48 bytes to three totaling 40 bytes, excluding map/cleanup internals and allocator size-class rounding. This is code-generation evidence, not a whole-process memory benchmark.

The standard-Go -race runs validate the public test harness against Go's runtime; they are not race-detector coverage of LLGo's runtime implementation.

Targeted CI verification

The complete previously failing Qiniu Ubuntu 24.04 large / LLVM 22 / Go 1.27 shard-0 job passed for this fix in run 34703427423, including all later integration checks. The original test, symbol, and build-mode phases completed in 136, 12, and 615 seconds; the job completed in 23m37s without ptrace sampling. The same target compiler and public stress source were then compiled against unmodified main runtime b07cd12ad: the helper hit its 60-second deadline, and the native stack validator confirmed on one thread two weak cleanup callbacks, two GC_invoke_finalizers frames, allocating mapaccess1_fast64, and the weakState mutex self-lock. Recompiled against the fixed runtime, the same 16-round/8,192-object stress test passed three times in 0.14–0.16 seconds per run.

The ordinary PR matrix for 50409da6e also completed with 64 successful checks, no failure, and only the expected tag-gated release job skipped. Its normal Qiniu shard-0 job independently passed in 22m26s, and Codecov reports all modified coverable lines covered. The subsequent a7863ee05 commit adds source comments only; the full-matrix results cited here are for 50409da6e, not a new CI run on the documentation commit.

#2567 was rebased onto this fix without changing its five panic-location patches. Its complete targeted Qiniu job independently passed in run 34702086188, including a 136-second first test phase, 12-second symbol check, 602-second build-mode checks, all later integration checks, and three 0.16-second weak-stress runs, again without ptrace sampling. The temporary diagnostic workflow in draft #2573 and its unrelated MakeFunc/continuous-GC starvation reproducer are not included in either production PR.

@cpunion

cpunion commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator Author

@fennoai Please review the weak-cleanup reentrancy fix, especially the one-time atomic pending-list publication, registration-time batch reclamation, and address-reuse identity/live checks. The same public-API pressure test fails 3/3 on the old runtime with matching native self-deadlock stacks and passes 53 times with default GC markers plus 20 times with one marker locally. Only the affected Qiniu job is currently being validated for this PR and dependent #2567; full matrices will follow after both targeted jobs pass.

@fennoai

fennoai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

FennoAI is processing your request, please wait a moment...

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: weak-cleanup reentrancy fix

The core design is sound and directly addresses the captured self-deadlock. Moving the map mutation and lock acquisition out of the GC-finalizer path — the cleanup now only does an atomic live store plus a lock-free CAS push onto the intrusive dead list (retireWeakHandle), with reclamation deferred to registration under the existing weakState.mu (drainWeakHandles) — correctly removes the recursive weakState.mu acquisition inside a finalizer that the native stacks showed deadlocking. The read path (llgoMakeStrongFromWeak) stays a single atomic load with no lock/map/alloc, so there is no hot-path regression.

The two focus areas hold up under review:

  • Address-reuse identity check (drainWeakHandles, weakState.m[h.key] == h) correctly prevents a delayed old cleanup from deleting a fresh handle after the encoded address is reused — matching the mirrored logic in mfinal.go.
  • Live check at registration (h != nil && live != 0) correctly rejects a handle that was marked dead but not yet published/drained, so a stale entry is never re-vended; its later push then finds m[key] != h and skips the delete. This interaction is the subtle case the PR targets and it is correct.

Findings below are hardening/accuracy items, not blockers. I did not run the suite; local verification numbers are as reported by the author.

Comment thread runtime/internal/lib/runtime/weak_llgo.go
Comment thread test/_stress/README.md Outdated
Comment thread runtime/internal/lib/runtime/weak_llgo.go
@cpunion
cpunion marked this pull request as ready for review September 12, 2026 16:15

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: weak-pointer cleanup reentrancy fix

The core fix is sound. Moving the map mutation out of the GC finalizer and onto a lock-free Treiber "dead" stack (atomic CAS push, no lock/no alloc) genuinely eliminates the mutex-reentrancy/allocate-in-finalizer deadlock, and it mirrors the proven freeCleanupSlot/popCleanupSlot pattern already in z_gc.go. The two reuse guards are exactly right: the live != 0 check at registration avoids handing back a handle a finalizer already marked dead, and the weakState.m[h.key] == h identity check prevents a stale handle from deleting a newer one that reused the same encoded address. Test coverage is thoughtful — TestPointerIdentity pins handle-identity semantics, and the stress test isolates the deadlock in a child process with an out-of-process hard deadline.

Two non-blocking observations are left inline. No correctness-breaking defect was found in the lock-free logic.

Comment thread runtime/internal/lib/runtime/weak_llgo.go
Comment thread runtime/internal/lib/runtime/weak_llgo.go Outdated
@codecov

codecov Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

LLGo baseline benchmarks

50409da6e017 | workflow run | long-term charts

Program measurements

Platform Workload File size vs base Text size vs base Build vs base Run vs base
Linux cprintf 19848 B 0 B / +0.0% 387 B 0 B / +0.0% 482.807 ms -54.37 ms / -10.1% (better) 1.298 ms -20.01 us / -1.5% (better)
Linux cprintf-lto 19600 B 0 B / +0.0% 368 B 0 B / +0.0% 485.116 ms -19.45 ms / -3.9% (better) 1.247 ms -31.9 us / -2.5% (better)
Linux fmtprintf 1628536 B +136 B / +0.008352% (worse) 498042 B 0 B / +0.0% 4.013 s -141.2 ms / -3.4% (better) 3.140 ms -83.42 us / -2.6% (better)
Linux fmtprintf-lto 1474232 B +128 B / +0.008683% (worse) 434538 B 0 B / +0.0% 11.132 s -481.5 ms / -4.1% (better) 2.972 ms -131.9 us / -4.2% (better)
Linux println 62968 B 0 B / +0.0% 15170 B 0 B / +0.0% 492.411 ms -18.84 ms / -3.7% (better) 1.577 ms -86.3 us / -5.2% (better)
Linux println-lto 54328 B 0 B / +0.0% 12371 B 0 B / +0.0% 741.200 ms +8.115 ms / +1.1% (worse) 1.561 ms +10.82 us / +0.7% (worse)
macOS cprintf 84480 B 0 B / +0.0% 17117 B 0 B / +0.0% 578.089 ms +3.929 ms / +0.7% (worse) 2.259 ms -185.8 us / -7.6% (better)
macOS cprintf-lto 84288 B 0 B / +0.0% 12881 B 0 B / +0.0% 549.232 ms -30.14 ms / -5.2% (better) 2.463 ms -177.2 us / -6.7% (better)
macOS fmtprintf 1473264 B 0 B / +0.0% 874544 B +128 B / +0.01464% (worse) 2.781 s +120.4 ms / +4.5% (worse) 4.565 ms -358.6 us / -7.3% (better)
macOS fmtprintf-lto 1159424 B 0 B / +0.0% 841700 B +124 B / +0.01473% (worse) 5.935 s -1.006 s / -14.5% (better) 4.292 ms -1.636 ms / -27.6% (better)
macOS println 114928 B 0 B / +0.0% 35228 B 0 B / +0.0% 499.875 ms -15.51 ms / -3.0% (better) 5.511 ms +2.245 ms / +68.8% (worse)
macOS println-lto 118736 B 0 B / +0.0% 32714 B 0 B / +0.0% 647.961 ms -64.23 ms / -9.0% (better) 2.872 ms -311.9 us / -9.8% (better)
Windows MinGW cprintf 19456 B 0 B / +0.0% 4550 B 0 B / +0.0% 1.138 s -4.522 ms / -0.4% (better) 3.386 ms -412.1 us / -10.8% (better)
Windows MinGW cprintf-lto 17920 B 0 B / +0.0% 4486 B 0 B / +0.0% 1.134 s -29.88 ms / -2.6% (better) 3.467 ms -61.5 us / -1.7% (better)
Windows MinGW fmtprintf 1898496 B 0 B / +0.0% 602022 B 0 B / +0.0% 4.069 s -49.42 ms / -1.2% (better) 8.117 ms -292.3 us / -3.5% (better)
Windows MinGW fmtprintf-lto 1934336 B 0 B / +0.0% 547222 B 0 B / +0.0% 10.027 s -313.7 ms / -3.0% (better) 7.899 ms -1.654 ms / -17.3% (better)
Windows MinGW println 72192 B 0 B / +0.0% 24358 B 0 B / +0.0% 1.120 s -14.53 ms / -1.3% (better) 6.293 ms -423.4 us / -6.3% (better)
Windows MinGW println-lto 65536 B 0 B / +0.0% 20710 B 0 B / +0.0% 1.326 s -41.93 ms / -3.1% (better) 6.381 ms -156.2 us / -2.4% (better)
Windows MinGW 386 cprintf 42496 B 0 B / +0.0% 5326 B 0 B / +0.0% 1.159 s +87.64 ms / +8.2% (worse) 5.034 ms +1.2 us / +0.02384% (worse)
Windows MinGW 386 cprintf-lto 20992 B 0 B / +0.0% 5094 B 0 B / +0.0% 1.084 s -8.284 ms / -0.8% (better) 4.926 ms -82.3 us / -1.6% (better)
Windows MinGW 386 fmtprintf 1861632 B 0 B / +0.0% 473806 B 0 B / +0.0% 3.998 s +78.24 ms / +2.0% (worse) 10.774 ms -112.1 us / -1.0% (better)
Windows MinGW 386 fmtprintf-lto 2163712 B +512 B / +0.02367% (worse) 449858 B 0 B / +0.0% 9.529 s +14.78 ms / +0.2% (worse) 10.852 ms +3.7 us / +0.03411% (worse)
Windows MinGW 386 println 91648 B 0 B / +0.0% 20422 B 0 B / +0.0% 1.072 s -2.012 ms / -0.2% (better) 8.580 ms +223.7 us / +2.7% (worse)
Windows MinGW 386 println-lto 70656 B 0 B / +0.0% 18274 B 0 B / +0.0% 1.301 s +27.77 ms / +2.2% (worse) 8.674 ms +126.6 us / +1.5% (worse)
Windows MinGW ARM64 cprintf 18944 B 0 B / +0.0% 4408 B 0 B / +0.0% 1.419 s -1.518 ms / -0.1% (better) 6.270 ms +304.6 us / +5.1% (worse)
Windows MinGW ARM64 cprintf-lto 17920 B 0 B / +0.0% 4340 B 0 B / +0.0% 1.476 s +21.74 ms / +1.5% (worse) 6.439 ms +108.3 us / +1.7% (worse)
Windows MinGW ARM64 fmtprintf 1786880 B +512 B / +0.02866% (worse) 513140 B 0 B / +0.0% 4.246 s +122.7 ms / +3.0% (worse) 13.193 ms +852.8 us / +6.9% (worse)
Windows MinGW ARM64 fmtprintf-lto 1862144 B 0 B / +0.0% 478112 B +8 B / +0.001673% (worse) 9.892 s +205.5 ms / +2.1% (worse) 12.928 ms +836 us / +6.9% (worse)
Windows MinGW ARM64 println 69120 B 0 B / +0.0% 23052 B 0 B / +0.0% 1.434 s +14.66 ms / +1.0% (worse) 10.945 ms -174.8 us / -1.6% (better)
Windows MinGW ARM64 println-lto 65024 B 0 B / +0.0% 20184 B 0 B / +0.0% 1.629 s +56.3 ms / +3.6% (worse) 11.086 ms +893 us / +8.8% (worse)
Windows MSVC cprintf 120320 B 0 B / +0.0% 65782 B 0 B / +0.0% 678.437 ms -3.115 ms / -0.5% (better) 2.723 ms -9.6 us / -0.4% (better)
Windows MSVC cprintf-lto 119808 B 0 B / +0.0% 65718 B 0 B / +0.0% 700.615 ms -6.766 ms / -1.0% (better) 2.687 ms -79.5 us / -2.9% (better)
Windows MSVC fmtprintf 1629184 B 0 B / +0.0% 697542 B 0 B / +0.0% 2.892 s +12.52 ms / +0.4% (worse) 7.043 ms +216 us / +3.2% (worse)
Windows MSVC fmtprintf-lto 1623040 B +512 B / +0.03156% (worse) 650118 B 0 B / +0.0% 7.029 s +53.27 ms / +0.8% (worse) 6.977 ms +294.1 us / +4.4% (worse)
Windows MSVC println 193024 B 0 B / +0.0% 119782 B 0 B / +0.0% 694.316 ms +14.16 ms / +2.1% (worse) 5.482 ms +5.8 us / +0.1% (worse)
Windows MSVC println-lto 189952 B 0 B / +0.0% 116646 B 0 B / +0.0% 824.917 ms -4.059 ms / -0.5% (better) 5.478 ms +70.7 us / +1.3% (worse)
Windows MSVC 386 cprintf 9728 B 0 B / +0.0% 3931 B 0 B / +0.0% 692.748 ms -5.895 ms / -0.8% (better) 4.635 ms +17 us / +0.4% (worse)
Windows MSVC 386 cprintf-lto 9216 B 0 B / +0.0% 3853 B 0 B / +0.0% 782.741 ms +6.281 ms / +0.8% (worse) 4.501 ms -137.6 us / -3.0% (better)
Windows MSVC 386 fmtprintf 1192448 B +512 B / +0.04296% (worse) 457173 B 0 B / +0.0% 3.064 s +64.06 ms / +2.1% (worse) 40.258 ms +237.9 us / +0.6% (worse)
Windows MSVC 386 fmtprintf-lto 1228800 B 0 B / +0.0% 429113 B 0 B / +0.0% 6.745 s +17.77 ms / +0.3% (worse) 9.775 ms -251.1 us / -2.5% (better)
Windows MSVC 386 println 34816 B 0 B / +0.0% 19265 B 0 B / +0.0% 699.604 ms +17.17 ms / +2.5% (worse) 7.306 ms -212.2 us / -2.8% (better)
Windows MSVC 386 println-lto 32768 B 0 B / +0.0% 17351 B 0 B / +0.0% 863.110 ms +34.77 ms / +4.2% (worse) 7.750 ms +9.7 us / +0.1% (worse)
Windows MSVC ARM64 cprintf 11264 B 0 B / +0.0% 3976 B 0 B / +0.0% 2.048 s -44.6 ms / -2.1% (better) 7.053 ms -655.5 us / -8.5% (better)
Windows MSVC ARM64 cprintf-lto 10752 B 0 B / +0.0% 3868 B 0 B / +0.0% 2.073 s -29.78 ms / -1.4% (better) 7.167 ms -294.1 us / -3.9% (better)
Windows MSVC ARM64 fmtprintf 1376768 B 0 B / +0.0% 514036 B 0 B / +0.0% 6.448 s -14.61 ms / -0.2% (better) 14.513 ms -689.9 us / -4.5% (better)
Windows MSVC ARM64 fmtprintf-lto 1397760 B 0 B / +0.0% 480788 B +16 B / +0.003328% (worse) 16.054 s +510.7 ms / +3.3% (worse) 14.895 ms -356 us / -2.3% (better)
Windows MSVC ARM64 println 41984 B 0 B / +0.0% 22312 B 0 B / +0.0% 2.024 s -30.88 ms / -1.5% (better) 12.289 ms -1.086 ms / -8.1% (better)
Windows MSVC ARM64 println-lto 40448 B 0 B / +0.0% 20092 B 0 B / +0.0% 2.313 s -100.8 ms / -4.2% (better) 12.622 ms +337.4 us / +2.7% (worse)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 14.750 ns/op -0.53 ns/op / -3.5% (better)
Linux BenchmarkMergeCompilerFlags 202.500 ns/op -1.1 ns/op / -0.5% (better)
Linux BenchmarkMergeLinkerFlags 150.100 ns/op +19.6 ns/op / +15.0% (worse)
Linux BenchmarkChannelBuffered 69.900 ns/op -0.06 ns/op / -0.1% (better)
Linux BenchmarkChannelHandoff 15007 ns/op +1463 ns/op / +10.8% (worse)
Linux BenchmarkDefer 47.200 ns/op -2.35 ns/op / -4.7% (better)
Linux BenchmarkDirectCall 1.943 ns/op -0.021 ns/op / -1.1% (better)
Linux BenchmarkGlobalRead 1.168 ns/op 0 ns/op / +0.0%
Linux BenchmarkGlobalWrite 7.780 ns/op +0.017 ns/op / +0.2% (worse)
Linux BenchmarkGoroutine 20913 ns/op -785 ns/op / -3.6% (better)
Linux BenchmarkInterfaceCall 5.877 ns/op -0.037 ns/op / -0.6% (better)
Linux BenchmarkRuntimeGetG 2.587 ns/op +0.147 ns/op / +6.0% (worse)
macOS BenchmarkLookupPCRandom 10.750 ns/op -1.43 ns/op / -11.7% (better)
macOS BenchmarkMergeCompilerFlags 84.710 ns/op -22.19 ns/op / -20.8% (better)
macOS BenchmarkMergeLinkerFlags 59.480 ns/op -8.33 ns/op / -12.3% (better)
macOS BenchmarkChannelBuffered 22.910 ns/op -4.15 ns/op / -15.3% (better)
macOS BenchmarkChannelHandoff 6563 ns/op -747 ns/op / -10.2% (better)
macOS BenchmarkDefer 28.810 ns/op -7.77 ns/op / -21.2% (better)
macOS BenchmarkDirectCall 1.098 ns/op +0.038 ns/op / +3.6% (worse)
macOS BenchmarkGlobalRead 0.942 ns/op -0.1188 ns/op / -11.2% (better)
macOS BenchmarkGlobalWrite 0.942 ns/op -0.1009 ns/op / -9.7% (better)
macOS BenchmarkGoroutine 32355 ns/op -26855 ns/op / -45.4% (better)
macOS BenchmarkInterfaceCall 4.084 ns/op +0.116 ns/op / +2.9% (worse)
macOS BenchmarkRuntimeGetG 1.891 ns/op -0.216 ns/op / -10.3% (better)
Windows MinGW BenchmarkLookupPCRandom 13.160 ns/op -0.04 ns/op / -0.3% (better)
Windows MinGW BenchmarkMergeCompilerFlags 648.100 ns/op +14.9 ns/op / +2.4% (worse)
Windows MinGW BenchmarkMergeLinkerFlags 575.800 ns/op +31.3 ns/op / +5.7% (worse)
Windows MinGW BenchmarkChannelBuffered 36.880 ns/op -0.28 ns/op / -0.8% (better)
Windows MinGW BenchmarkChannelHandoff 979.300 ns/op -108.7 ns/op / -10.0% (better)
Windows MinGW BenchmarkDefer 59.470 ns/op +0.88 ns/op / +1.5% (worse)
Windows MinGW BenchmarkDirectCall 1.548 ns/op +0.001 ns/op / +0.1% (worse)
Windows MinGW BenchmarkGlobalRead 1.549 ns/op +0.001 ns/op / +0.1% (worse)
Windows MinGW BenchmarkGlobalWrite 2.472 ns/op -0.004 ns/op / -0.2% (better)
Windows MinGW BenchmarkGoroutine 81613 ns/op +4127 ns/op / +5.3% (worse)
Windows MinGW BenchmarkInterfaceCall 8.060 ns/op -0.007 ns/op / -0.1% (better)
Windows MinGW BenchmarkRuntimeGetG 2.485 ns/op +0.004 ns/op / +0.2% (worse)
Windows MinGW 386 BenchmarkLookupPCRandom 27.830 ns/op +0.07 ns/op / +0.3% (worse)
Windows MinGW 386 BenchmarkMergeCompilerFlags 731.200 ns/op -67.5 ns/op / -8.5% (better)
Windows MinGW 386 BenchmarkMergeLinkerFlags 675.700 ns/op -40.9 ns/op / -5.7% (better)
Windows MinGW 386 BenchmarkChannelBuffered 47.410 ns/op +0.18 ns/op / +0.4% (worse)
Windows MinGW 386 BenchmarkChannelHandoff 804.800 ns/op -180.4 ns/op / -18.3% (better)
Windows MinGW 386 BenchmarkDefer 44.260 ns/op -2.04 ns/op / -4.4% (better)
Windows MinGW 386 BenchmarkDirectCall 1.745 ns/op -0.002 ns/op / -0.1% (better)
Windows MinGW 386 BenchmarkGlobalRead 1.751 ns/op +0.003 ns/op / +0.2% (worse)
Windows MinGW 386 BenchmarkGlobalWrite 8.983 ns/op -0.002 ns/op / -0.02226% (better)
Windows MinGW 386 BenchmarkGoroutine 71799 ns/op +1158 ns/op / +1.6% (worse)
Windows MinGW 386 BenchmarkInterfaceCall 9.440 ns/op -0.004 ns/op / -0.04235% (better)
Windows MinGW 386 BenchmarkRuntimeGetG 2.450 ns/op +0.003 ns/op / +0.1% (worse)
Windows MinGW ARM64 BenchmarkLookupPCRandom 12.070 ns/op -0.02 ns/op / -0.2% (better)
Windows MinGW ARM64 BenchmarkMergeCompilerFlags 571.900 ns/op -4.5 ns/op / -0.8% (better)
Windows MinGW ARM64 BenchmarkMergeLinkerFlags 540.900 ns/op +16.8 ns/op / +3.2% (worse)
Windows MinGW ARM64 BenchmarkChannelBuffered 43.770 ns/op -2.57 ns/op / -5.5% (better)
Windows MinGW ARM64 BenchmarkChannelHandoff 1957 ns/op +186 ns/op / +10.5% (worse)
Windows MinGW ARM64 BenchmarkDefer 54.850 ns/op -1.28 ns/op / -2.3% (better)
Windows MinGW ARM64 BenchmarkDirectCall 0.589 ns/op -0.0003 ns/op / -0.1% (better)
Windows MinGW ARM64 BenchmarkGlobalRead 0.885 ns/op +0.0002 ns/op / +0.02261% (worse)
Windows MinGW ARM64 BenchmarkGlobalWrite 0.664 ns/op +0.0005 ns/op / +0.1% (worse)
Windows MinGW ARM64 BenchmarkGoroutine 59086 ns/op -113 ns/op / -0.2% (better)
Windows MinGW ARM64 BenchmarkInterfaceCall 4.142 ns/op +0.001 ns/op / +0.02415% (worse)
Windows MinGW ARM64 BenchmarkRuntimeGetG 1.810 ns/op +0.002 ns/op / +0.1% (worse)
Windows MSVC BenchmarkLookupPCRandom 9.529 ns/op -0.091 ns/op / -0.9% (better)
Windows MSVC BenchmarkMergeCompilerFlags 381.100 ns/op -10.2 ns/op / -2.6% (better)
Windows MSVC BenchmarkMergeLinkerFlags 367.600 ns/op +31.6 ns/op / +9.4% (worse)
Windows MSVC BenchmarkChannelBuffered 30.790 ns/op -0.03 ns/op / -0.1% (better)
Windows MSVC BenchmarkChannelHandoff 1190 ns/op +102 ns/op / +9.4% (worse)
Windows MSVC BenchmarkDefer 42.010 ns/op -0.32 ns/op / -0.8% (better)
Windows MSVC BenchmarkDirectCall 1.356 ns/op -0.001 ns/op / -0.1% (better)
Windows MSVC BenchmarkGlobalRead 1.357 ns/op +0.001 ns/op / +0.1% (worse)
Windows MSVC BenchmarkGlobalWrite 2.165 ns/op 0 ns/op / +0.0%
Windows MSVC BenchmarkGoroutine 49847 ns/op +1005 ns/op / +2.1% (worse)
Windows MSVC BenchmarkInterfaceCall 6.830 ns/op +0.003 ns/op / +0.04394% (worse)
Windows MSVC BenchmarkRuntimeGetG 1.407 ns/op +0.001 ns/op / +0.1% (worse)
Windows MSVC 386 BenchmarkLookupPCRandom 53.400 ns/op +1.4 ns/op / +2.7% (worse)
Windows MSVC 386 BenchmarkMergeCompilerFlags 513.300 ns/op -36.7 ns/op / -6.7% (better)
Windows MSVC 386 BenchmarkMergeLinkerFlags 542.900 ns/op +17.6 ns/op / +3.4% (worse)
Windows MSVC 386 BenchmarkChannelBuffered 44.150 ns/op -0.26 ns/op / -0.6% (better)
Windows MSVC 386 BenchmarkChannelHandoff 2761 ns/op -94 ns/op / -3.3% (better)
Windows MSVC 386 BenchmarkDefer 33.190 ns/op +0.1 ns/op / +0.3% (worse)
Windows MSVC 386 BenchmarkDirectCall 0.358 ns/op -0.0008 ns/op / -0.2% (better)
Windows MSVC 386 BenchmarkGlobalRead 0.696 ns/op +0.0053 ns/op / +0.8% (worse)
Windows MSVC 386 BenchmarkGlobalWrite 13.570 ns/op +0.89 ns/op / +7.0% (worse)
Windows MSVC 386 BenchmarkGoroutine 71390 ns/op +31 ns/op / +0.04344% (worse)
Windows MSVC 386 BenchmarkInterfaceCall 4.160 ns/op +0.038 ns/op / +0.9% (worse)
Windows MSVC 386 BenchmarkRuntimeGetG 0.968 ns/op +0.0073 ns/op / +0.8% (worse)
Windows MSVC ARM64 BenchmarkLookupPCRandom 12.030 ns/op -0.02 ns/op / -0.2% (better)
Windows MSVC ARM64 BenchmarkMergeCompilerFlags 557.400 ns/op -6.2 ns/op / -1.1% (better)
Windows MSVC ARM64 BenchmarkMergeLinkerFlags 534.800 ns/op +9.2 ns/op / +1.8% (worse)
Windows MSVC ARM64 BenchmarkChannelBuffered 46.920 ns/op +2.48 ns/op / +5.6% (worse)
Windows MSVC ARM64 BenchmarkChannelHandoff 2157 ns/op +96 ns/op / +4.7% (worse)
Windows MSVC ARM64 BenchmarkDefer 66.340 ns/op +1.87 ns/op / +2.9% (worse)
Windows MSVC ARM64 BenchmarkDirectCall 0.590 ns/op +0.0003 ns/op / +0.1% (worse)
Windows MSVC ARM64 BenchmarkGlobalRead 0.589 ns/op 0 ns/op / +0.0%
Windows MSVC ARM64 BenchmarkGlobalWrite 3.757 ns/op +0.002 ns/op / +0.1% (worse)
Windows MSVC ARM64 BenchmarkGoroutine 53214 ns/op -54 ns/op / -0.1% (better)
Windows MSVC ARM64 BenchmarkInterfaceCall 4.135 ns/op -0.012 ns/op / -0.3% (better)
Windows MSVC ARM64 BenchmarkRuntimeGetG 1.784 ns/op -0.193 ns/op / -9.8% (better)

Timer runtime benchmarks

Platform Operation and runtime ns/op vs base
Linux AfterFuncZeroDelivery/Go 913.700 ns/op +4.5 ns/op / +0.5% (worse)
Linux AfterFuncZeroDelivery/LLGo 32315 ns/op -1798 ns/op / -5.3% (better)
Linux CreateStop/Go 290.300 ns/op -13.9 ns/op / -4.6% (better)
Linux CreateStop/LLGo 1729 ns/op -21 ns/op / -1.2% (better)
Linux RearmStopped/Go 116.200 ns/op +0.2 ns/op / +0.2% (worse)
Linux RearmStopped/LLGo 1104 ns/op -195 ns/op / -15.0% (better)
Linux ResetActive/Go 69.470 ns/op +0.68 ns/op / +1.0% (worse)
Linux ResetActive/LLGo 847.400 ns/op +244.1 ns/op / +40.5% (worse)
Linux ResetHeap1024/Go 67.250 ns/op -0.27 ns/op / -0.4% (better)
Linux ResetHeap1024/LLGo 179.600 ns/op -3.2 ns/op / -1.8% (better)
macOS AfterFuncZeroDelivery/Go 368.800 ns/op -106 ns/op / -22.3% (better)
macOS AfterFuncZeroDelivery/LLGo 57434 ns/op -3548 ns/op / -5.8% (better)
macOS CreateStop/Go 116 ns/op -16.3 ns/op / -12.3% (better)
macOS CreateStop/LLGo 424.100 ns/op -15.4 ns/op / -3.5% (better)
macOS RearmStopped/Go 51.210 ns/op -14.56 ns/op / -22.1% (better)
macOS RearmStopped/LLGo 328.300 ns/op +22.4 ns/op / +7.3% (worse)
macOS ResetActive/Go 37.570 ns/op -4 ns/op / -9.6% (better)
macOS ResetActive/LLGo 161.400 ns/op +14.6 ns/op / +9.9% (worse)
macOS ResetHeap1024/Go 37.500 ns/op -10.52 ns/op / -21.9% (better)
macOS ResetHeap1024/LLGo 75.950 ns/op -12.25 ns/op / -13.9% (better)
Windows MinGW AfterFuncZeroDelivery/Go 552.500 ns/op -22.9 ns/op / -4.0% (better)
Windows MinGW AfterFuncZeroDelivery/LLGo 161182 ns/op -2296 ns/op / -1.4% (better)
Windows MinGW CreateStop/Go 115 ns/op -0.7 ns/op / -0.6% (better)
Windows MinGW CreateStop/LLGo 437.800 ns/op +0.1 ns/op / +0.02285% (worse)
Windows MinGW RearmStopped/Go 31.320 ns/op -0.21 ns/op / -0.7% (better)
Windows MinGW RearmStopped/LLGo 271.300 ns/op +1.7 ns/op / +0.6% (worse)
Windows MinGW ResetActive/Go 20.100 ns/op -0.02 ns/op / -0.1% (better)
Windows MinGW ResetActive/LLGo 173.700 ns/op +13.3 ns/op / +8.3% (worse)
Windows MinGW ResetHeap1024/Go 20.310 ns/op -0.08 ns/op / -0.4% (better)
Windows MinGW ResetHeap1024/LLGo 131.400 ns/op -0.8 ns/op / -0.6% (better)
Windows MinGW 386 AfterFuncZeroDelivery/Go 988.600 ns/op +4.5 ns/op / +0.5% (worse)
Windows MinGW 386 AfterFuncZeroDelivery/LLGo 158031 ns/op +2099 ns/op / +1.3% (worse)
Windows MinGW 386 CreateStop/Go 221.700 ns/op +5.8 ns/op / +2.7% (worse)
Windows MinGW 386 CreateStop/LLGo 2847 ns/op +343 ns/op / +13.7% (worse)
Windows MinGW 386 RearmStopped/Go 73.270 ns/op +0.32 ns/op / +0.4% (worse)
Windows MinGW 386 RearmStopped/LLGo 374.500 ns/op +15.1 ns/op / +4.2% (worse)
Windows MinGW 386 ResetActive/Go 41.990 ns/op -0.05 ns/op / -0.1% (better)
Windows MinGW 386 ResetActive/LLGo 907.600 ns/op -56 ns/op / -5.8% (better)
Windows MinGW 386 ResetHeap1024/Go 42.260 ns/op -0.06 ns/op / -0.1% (better)
Windows MinGW 386 ResetHeap1024/LLGo 194.400 ns/op +1.8 ns/op / +0.9% (worse)
Windows MinGW ARM64 AfterFuncZeroDelivery/Go 665.100 ns/op -2.5 ns/op / -0.4% (better)
Windows MinGW ARM64 AfterFuncZeroDelivery/LLGo 121523 ns/op +5247 ns/op / +4.5% (worse)
Windows MinGW ARM64 CreateStop/Go 200.500 ns/op -4.5 ns/op / -2.2% (better)
Windows MinGW ARM64 CreateStop/LLGo 362.100 ns/op +5.8 ns/op / +1.6% (worse)
Windows MinGW ARM64 RearmStopped/Go 70.530 ns/op -0.03 ns/op / -0.04252% (better)
Windows MinGW ARM64 RearmStopped/LLGo 253.300 ns/op +3.5 ns/op / +1.4% (worse)
Windows MinGW ARM64 ResetActive/Go 31.090 ns/op +0.06 ns/op / +0.2% (worse)
Windows MinGW ARM64 ResetActive/LLGo 132.200 ns/op +3.1 ns/op / +2.4% (worse)
Windows MinGW ARM64 ResetHeap1024/Go 31.130 ns/op +0.11 ns/op / +0.4% (worse)
Windows MinGW ARM64 ResetHeap1024/LLGo 127.300 ns/op +0.8 ns/op / +0.6% (worse)
Windows MSVC AfterFuncZeroDelivery/Go 371.300 ns/op -5.5 ns/op / -1.5% (better)
Windows MSVC AfterFuncZeroDelivery/LLGo 97273 ns/op -7956 ns/op / -7.6% (better)
Windows MSVC CreateStop/Go 90.840 ns/op +1.23 ns/op / +1.4% (worse)
Windows MSVC CreateStop/LLGo 319.400 ns/op -20.7 ns/op / -6.1% (better)
Windows MSVC RearmStopped/Go 24.460 ns/op -0.01 ns/op / -0.04087% (better)
Windows MSVC RearmStopped/LLGo 217.100 ns/op +5.9 ns/op / +2.8% (worse)
Windows MSVC ResetActive/Go 14.760 ns/op -0.04 ns/op / -0.3% (better)
Windows MSVC ResetActive/LLGo 115.500 ns/op +3.1 ns/op / +2.8% (worse)
Windows MSVC ResetHeap1024/Go 14.780 ns/op 0 ns/op / +0.0%
Windows MSVC ResetHeap1024/LLGo 102.300 ns/op -1.7 ns/op / -1.6% (better)
Windows MSVC 386 AfterFuncZeroDelivery/Go 694 ns/op +0.6 ns/op / +0.1% (worse)
Windows MSVC 386 AfterFuncZeroDelivery/LLGo 312863 ns/op +6678 ns/op / +2.2% (worse)
Windows MSVC 386 CreateStop/Go 178 ns/op -0.6 ns/op / -0.3% (better)
Windows MSVC 386 CreateStop/LLGo 61000 ns/op +1279 ns/op / +2.1% (worse)
Windows MSVC 386 RearmStopped/Go 64.970 ns/op -0.1 ns/op / -0.2% (better)
Windows MSVC 386 RearmStopped/LLGo 297.600 ns/op +35.7 ns/op / +13.6% (worse)
Windows MSVC 386 ResetActive/Go 31.050 ns/op -0.04 ns/op / -0.1% (better)
Windows MSVC 386 ResetActive/LLGo 209.700 ns/op -39.7 ns/op / -15.9% (better)
Windows MSVC 386 ResetHeap1024/Go 31.540 ns/op +0.05 ns/op / +0.2% (worse)
Windows MSVC 386 ResetHeap1024/LLGo 136.500 ns/op +0.1 ns/op / +0.1% (worse)
Windows MSVC ARM64 AfterFuncZeroDelivery/Go 670.500 ns/op -7.8 ns/op / -1.1% (better)
Windows MSVC ARM64 AfterFuncZeroDelivery/LLGo 126401 ns/op -9608 ns/op / -7.1% (better)
Windows MSVC ARM64 CreateStop/Go 198 ns/op -2 ns/op / -1.0% (better)
Windows MSVC ARM64 CreateStop/LLGo 386.700 ns/op +14.4 ns/op / +3.9% (worse)
Windows MSVC ARM64 RearmStopped/Go 70.620 ns/op +0.08 ns/op / +0.1% (worse)
Windows MSVC ARM64 RearmStopped/LLGo 269.400 ns/op -2.6 ns/op / -1.0% (better)
Windows MSVC ARM64 ResetActive/Go 31.090 ns/op +0.01 ns/op / +0.03218% (worse)
Windows MSVC ARM64 ResetActive/LLGo 138.200 ns/op +3.5 ns/op / +2.6% (worse)
Windows MSVC ARM64 ResetHeap1024/Go 31.170 ns/op -0.09 ns/op / -0.3% (better)
Windows MSVC ARM64 ResetHeap1024/LLGo 138.100 ns/op -0.2 ns/op / -0.1% (better)

Compared with b07cd12ad1ac measured in the same runner job.

@github-actions

Copy link
Copy Markdown

LLGo WebAssembly build benchmarks

50409da6e017 | workflow run | long-term charts

WebAssembly output sizes

Profile and compiler Wasm module vs base Generated JS glue vs base
ec32/LLGo 113546 B 0 B / +0.0% 70736 B 0 B / +0.0%
ec64/LLGo 119001 B 0 B / +0.0% 74033 B 0 B / +0.0%
js/Go 1895533 B 0 B / +0.0% 0 B 0 B / 0.0%
js/LLGo 66698 B 0 B / +0.0% 68511 B 0 B / +0.0%
wasip1/Go 1909947 B 0 B / +0.0% 0 B 0 B / 0.0%
wasip1/LLGo 72937 B 0 B / +0.0% 0 B 0 B / 0.0%
wc32/LLGo 117537 B 0 B / +0.0% 0 B 0 B / 0.0%

LLGo WebAssembly build measurements

Profile Build vs base
ec32 5.562 s -201.9 ms / -3.5% (better)
ec64 5.151 s -36.22 ms / -0.7% (better)
js 4.529 s -117.5 ms / -2.5% (better)
wasip1 3.018 s -227.3 ms / -7.0% (better)
wc32 3.994 s +105.9 ms / +2.7% (worse)

Compared with b07cd12ad1ac measured in the same runner job.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant