Skip to content

PR 4: Comparison expansion (Threading.Channels + Nim Channel + MoodyCamel)#23

Closed
elijahr wants to merge 10 commits intofeat/bench-rollup-pr3-comparison-mvpfrom
feat/bench-rollup-pr4-comparison-expansion
Closed

PR 4: Comparison expansion (Threading.Channels + Nim Channel + MoodyCamel)#23
elijahr wants to merge 10 commits intofeat/bench-rollup-pr3-comparison-mvpfrom
feat/bench-rollup-pr4-comparison-expansion

Conversation

@elijahr
Copy link
Copy Markdown
Owner

@elijahr elijahr commented May 1, 2026

Track 4 of the bench-rollup feature. Extends the comparison set from
PR 3's 5 adapters to 9 (across 7 upstream libraries) so each topology
has 3-or-more distinct implementations plotted on the Bencher
dashboard. The vendored MoodyCamel header lands the first vendored
third-party entry in THIRD_PARTY_LICENSES.md and proves the
vendoring pattern for PR 5's uPlot bundle.

Depends on PR #19, PR #20, PR #21, PR #22.

Adapters added (4)

  • moodycamel_adapter.nim — MoodyCamel ConcurrentQueue (BSD/Boost
    dual, mpmc_unbounded). Vendored single-header at
    benchmarks/vendor/concurrentqueue/concurrentqueue.h (pinned to
    upstream commit d655418bb644b7f85159d94c591d7d983949fb81); a
    thin extern "C" shim (moodycamel_wrapper.cpp) isolates Nim from
    upstream's template machinery (Risk M5).
  • threading_channels_adapter.nim — nimble threading package's
    Chan[T] (MIT, mpmc bounded). Non-blocking trySend /
    tryRecv.
  • nim_channel_adapter.nim — Nim stdlib system.Channel[T] (MIT,
    mpsc bounded). Blocking-on-full producer; the apples-to-oranges
    fairness caveat is documented inline and the chart legend marks
    the slug accordingly.

Plus a smoke for each new third-party adapter
(smoke_moodycamel.nim, smoke_threading_channels.nim) and a third
gate in tests/t_bench_adapters.nim covering 1000-item push/pop
round-trip set equality.

Bench wiring

  • bench_mpmc.nim emits threading_channels/mpmc/{1,2,4}p{1,2,4}c
    (9 shapes) when -d:adapter_threading_channels_available.
  • bench_mpsc.nim emits nim_channel/mpsc/{1,2,4}p1c (3 shapes)
    when -d:adapter_nim_channel_available. New: bench_mpsc compile
    step now consumes ADAPTER_FLAGS (it was unused in PR 3 because
    no MVP adapter targeted MPSC).
  • bench_unbounded.nim emits
    moodycamel/ConcurrentQueue/mpmc_unbounded/{1,2,4}p{1,2,4}c (9
    shapes) when -d:adapter_moodycamel_available. Compile step
    switches from hard-coded nim c to \$NIM_MODE so MoodyCamel can
    request nim cpp while loony continues to be mode-neutral.

Default builds without any gate continue to compile clean and emit
the existing pre-PR-4 slug set; verified by per-binary smoke runs at
1000 messages.

CI

bench.yml extends with three new workflow_dispatch boolean inputs
(force_skip_moodycamel / force_skip_threading_channels /
force_skip_nim_channel) and per-library install → smoke → set-flag
pipelines mirroring the PR-3 soft-skip pattern (design §2.6).
MoodyCamel's "install" step is a test -f against the vendored
header so the bench is reproducible without network egress.
actionlint clean.

Cross-cutting

  • THIRD_PARTY_LICENSES.md lands its first vendored entry
    (concurrentqueue (MoodyCamel), BSD/Boost dual, pinned SHA
    d655418bb644b7f85159d94c591d7d983949fb81) plus unvendored
    entries for the nimble threading package and Nim
    system.Channel.
  • New .gitattributes rule
    benchmarks/vendor/** linguist-vendored=true linguist-generated=true
    excludes the vendored MoodyCamel header from GitHub language stats.
    Verified via git check-attr.
  • benchmarks/README.md comparison table extends from 5 to 8 rows
    with install commands and the apples-to-oranges fairness asterisk
    for nim_channel/*.
  • CHANGELOG.md [Unreleased] gains the PR-4 / Track-4 bullets.

Verification

  • nimble test: 200 OK / 0 FAILED / 0 SKIPPED.
  • Per-adapter smokes:
    ./.tmp/smoke_moodycamel → 32 push/pop OK.
    ./.tmp/smoke_threading_channels → 32 push/pop OK.
  • Per-adapter t_bench_adapters round-trip: all three new gates
    (adapter_moodycamel_available under nim cpp,
    adapter_threading_channels_available,
    adapter_nim_channel_available) pass 1 OK / 0 FAILED.
  • Slug emission verified by smoke runs at 1000 messages — every
    expected slug is present in the BMF output.

Force-skip evidence

The three new force_skip_<lib> workflow_dispatch inputs are wired
through to the per-library install step's if: guard the same way
PR 3's force_skip_boost / force_skip_loony are wired. They have
NOT been exercised on workflow_dispatch from this PR yet because
the workflow runs against the merge target on push; once this PR is
merged the maintainer will run workflow_dispatch with each of the
three flags set in turn and record the resulting ::warning title= Adapter skipped::... annotations as evidence on the next release.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces three new third-party adapters to the benchmark suite: MoodyCamel's ConcurrentQueue, Nim's stdlib system.Channel, and the threading package's Chan. The changes include vendoring the MoodyCamel header, implementing C++ shims and Nim adapters, and updating CI configurations and documentation. Feedback focuses on improving the MoodycamelAdapter by adding static assertions to ensure type safety for the 64-bit payload in push and pop operations, and dynamically reflecting the generic type in the name procedure for consistency.

Comment on lines +69 to +72
proc push*[T](a: var MoodycamelAdapter[T], item: T): PushResult =
if a.queue == nil:
return prFull
if mc_push(a.queue, culonglong(uint64(item))) != cint(0):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The MoodycamelAdapter is generic over [T], but the underlying C++ wrapper mc_push is hardcoded to uint64_t (via culonglong). The current implementation uses uint64(item), which only works for ordinal types and will fail to compile for pointers or structs. Since the benchmark harness specifically targets uint64 payloads, you should add a static assertion to ensure type compatibility and prevent misuse with unsupported types.

  proc push*[T](a: var MoodycamelAdapter[T], item: T): PushResult =
    static: doAssert sizeof(T) <= 8, "MoodycamelAdapter only supports types up to 8 bytes"
    if a.queue == nil:
      return prFull
    if mc_push(a.queue, culonglong(uint64(item))) != cint(0):

Comment on lines +88 to +89
proc name*[T](a: MoodycamelAdapter[T]): string =
"moodycamel/ConcurrentQueue[uint64]"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The name procedure hardcodes [uint64] in the string. For consistency with other adapters in this PR (like NimChannelAdapter and ThreadingChannelsAdapter), it should use the string representation of the generic type parameter $T.

  proc name*[T](a: MoodycamelAdapter[T]): string =
    "moodycamel/ConcurrentQueue[" & $T & "]"

Comment on lines +79 to +84
proc pop*[T](a: var MoodycamelAdapter[T]): PopResult[T] =
if a.queue == nil:
return PopResult[T](success: false)
var raw: culonglong
if mc_pop(a.queue, addr raw) != cint(0):
PopResult[T](success: true, value: T(uint64(raw)))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Similar to the push procedure, the pop implementation performs a type conversion T(uint64(raw)) which assumes T is compatible with a 64-bit unsigned integer. A static assertion should be added here as well to ensure type safety.

  proc pop*[T](a: var MoodycamelAdapter[T]): PopResult[T] =
    static: doAssert sizeof(T) <= 8
    if a.queue == nil:
      return PopResult[T](success: false)
    var raw: culonglong
    if mc_pop(a.queue, addr raw) != cint(0):
      PopResult[T](success: true, value: T(uint64(raw)))

elijahr added a commit that referenced this pull request May 1, 2026
merge_bmf.py rejects slugs that aren't <library>/<topology>/<P>p<C>c
(exactly 3 segments). PR #23's CI failed on the moodycamel slug:

  invalid slug shape 'moodycamel/ConcurrentQueue/mpmc_unbounded/1p1c'

The same 4-segment violations exist for loony and crossbeam_seg_queue
in bench_unbounded.nim, and the crossbeam_array_queue dispatch was
emitting a colliding "crossbeam_queue" lib name. Apply the canonical
lib slugs from the design doc so all dispatch sites yield 3 segments
after the runner appends "/<topology>/<shape>":

  loony/LoonyQueue        -> loony
  crossbeam_queue/SegQueue -> crossbeam_seg_queue
  crossbeam_queue (Array)  -> crossbeam_array_queue
  moodycamel/ConcurrentQueue -> moodycamel

The PR #22 worktree applies the loony and crossbeam fixes too;
git rebase will harmonize the duplicate edits.

The adapter `name` constants (e.g. "moodycamel/ConcurrentQueue[uint64]")
are human-readable echo strings, not BMF slugs, and stay as-is.
@elijahr elijahr force-pushed the feat/bench-rollup-pr3-comparison-mvp branch from 8b7a87f to eb67e2b Compare May 1, 2026 18:13
elijahr added a commit that referenced this pull request May 1, 2026
merge_bmf.py rejects slugs that aren't <library>/<topology>/<P>p<C>c
(exactly 3 segments). PR #23's CI failed on the moodycamel slug:

  invalid slug shape 'moodycamel/ConcurrentQueue/mpmc_unbounded/1p1c'

The same 4-segment violations exist for loony and crossbeam_seg_queue
in bench_unbounded.nim, and the crossbeam_array_queue dispatch was
emitting a colliding "crossbeam_queue" lib name. Apply the canonical
lib slugs from the design doc so all dispatch sites yield 3 segments
after the runner appends "/<topology>/<shape>":

  loony/LoonyQueue        -> loony
  crossbeam_queue/SegQueue -> crossbeam_seg_queue
  crossbeam_queue (Array)  -> crossbeam_array_queue
  moodycamel/ConcurrentQueue -> moodycamel

The PR #22 worktree applies the loony and crossbeam fixes too;
git rebase will harmonize the duplicate edits.

The adapter `name` constants (e.g. "moodycamel/ConcurrentQueue[uint64]")
are human-readable echo strings, not BMF slugs, and stay as-is.
@elijahr elijahr force-pushed the feat/bench-rollup-pr4-comparison-expansion branch from 9738889 to 5e3d07b Compare May 1, 2026 18:15
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

🐰 Bencher Report

Branchfeat/bench-rollup-pr4-comparison-expansion
Testbedubuntu-latest

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds flag.

Click to view all benchmark results
Benchmarklatency_p50_nsMeasure (units)latency_p95_nsMeasure (units)latency_p99_nsMeasure (units)throughput_ops_msMeasure (units)
lockfreequeues_mupmuc/mpmc/1p1c📈 view plot
⚠️ NO THRESHOLD
295.09 units📈 view plot
⚠️ NO THRESHOLD
331.00 units📈 view plot
⚠️ NO THRESHOLD
354.09 units📈 view plot
⚠️ NO THRESHOLD
32,490.88 units
lockfreequeues_mupmuc/mpmc/1p2c📈 view plot
⚠️ NO THRESHOLD
29,526.77 units
lockfreequeues_mupmuc/mpmc/1p4c📈 view plot
⚠️ NO THRESHOLD
18,408.41 units
lockfreequeues_mupmuc/mpmc/2p1c📈 view plot
⚠️ NO THRESHOLD
22,085.62 units
lockfreequeues_mupmuc/mpmc/2p2c📈 view plot
⚠️ NO THRESHOLD
16,878.33 units
lockfreequeues_mupmuc/mpmc/2p4c📈 view plot
⚠️ NO THRESHOLD
16,667.94 units
lockfreequeues_mupmuc/mpmc/4p1c📈 view plot
⚠️ NO THRESHOLD
12,309.54 units
lockfreequeues_mupmuc/mpmc/4p2c📈 view plot
⚠️ NO THRESHOLD
12,101.74 units
lockfreequeues_mupmuc/mpmc/4p4c📈 view plot
⚠️ NO THRESHOLD
10,551.97 units
lockfreequeues_mupmuc/mpmc/8p8c📈 view plot
⚠️ NO THRESHOLD
10,234.92 units
lockfreequeues_mupsic/mpsc/1p1c📈 view plot
⚠️ NO THRESHOLD
280.82 units📈 view plot
⚠️ NO THRESHOLD
311.00 units📈 view plot
⚠️ NO THRESHOLD
336.00 units📈 view plot
⚠️ NO THRESHOLD
32,531.47 units
lockfreequeues_mupsic/mpsc/2p1c📈 view plot
⚠️ NO THRESHOLD
22,456.51 units
lockfreequeues_mupsic/mpsc/4p1c📈 view plot
⚠️ NO THRESHOLD
12,284.01 units
lockfreequeues_sipmuc/mpmc/1p1c📈 view plot
⚠️ NO THRESHOLD
261.00 units📈 view plot
⚠️ NO THRESHOLD
311.00 units📈 view plot
⚠️ NO THRESHOLD
339.82 units📈 view plot
⚠️ NO THRESHOLD
34,458.68 units
lockfreequeues_sipmuc/mpmc/1p2c📈 view plot
⚠️ NO THRESHOLD
31,172.83 units
lockfreequeues_sipmuc/mpmc/1p4c📈 view plot
⚠️ NO THRESHOLD
18,518.81 units
lockfreequeues_sipsic/spsc/1p1c📈 view plot
⚠️ NO THRESHOLD
386.91 units📈 view plot
⚠️ NO THRESHOLD
430.82 units📈 view plot
⚠️ NO THRESHOLD
459.09 units📈 view plot
⚠️ NO THRESHOLD
8,334.69 units
lockfreequeues_unbounded_mupmuc/mpmc_unbounded/1p1c📈 view plot
⚠️ NO THRESHOLD
10,295.46 units
lockfreequeues_unbounded_mupmuc/mpmc_unbounded/1p2c📈 view plot
⚠️ NO THRESHOLD
6,045.28 units
lockfreequeues_unbounded_mupmuc/mpmc_unbounded/2p1c📈 view plot
⚠️ NO THRESHOLD
10,364.10 units
lockfreequeues_unbounded_mupmuc/mpmc_unbounded/2p2c📈 view plot
⚠️ NO THRESHOLD
9,762.87 units
lockfreequeues_unbounded_mupsic/mpsc_unbounded/1p1c📈 view plot
⚠️ NO THRESHOLD
9,655.19 units
lockfreequeues_unbounded_mupsic/mpsc_unbounded/2p1c📈 view plot
⚠️ NO THRESHOLD
10,086.59 units
lockfreequeues_unbounded_mupsic/mpsc_unbounded/4p1c📈 view plot
⚠️ NO THRESHOLD
10,746.01 units
lockfreequeues_unbounded_sipmuc/mpmc_unbounded/1p1c📈 view plot
⚠️ NO THRESHOLD
17,464.81 units
lockfreequeues_unbounded_sipmuc/mpmc_unbounded/1p2c📈 view plot
⚠️ NO THRESHOLD
13,492.21 units
lockfreequeues_unbounded_sipsic/spsc_unbounded/1p1c📈 view plot
⚠️ NO THRESHOLD
14,637.77 units
moodycamel/mpmc_unbounded/1p1c📈 view plot
⚠️ NO THRESHOLD
19,971.23 units
moodycamel/mpmc_unbounded/1p2c📈 view plot
⚠️ NO THRESHOLD
11,636.53 units
moodycamel/mpmc_unbounded/1p4c📈 view plot
⚠️ NO THRESHOLD
12,814.36 units
moodycamel/mpmc_unbounded/2p1c📈 view plot
⚠️ NO THRESHOLD
30,768.21 units
moodycamel/mpmc_unbounded/2p2c📈 view plot
⚠️ NO THRESHOLD
11,663.35 units
moodycamel/mpmc_unbounded/2p4c📈 view plot
⚠️ NO THRESHOLD
13,231.14 units
moodycamel/mpmc_unbounded/4p1c📈 view plot
⚠️ NO THRESHOLD
33,450.49 units
moodycamel/mpmc_unbounded/4p2c📈 view plot
⚠️ NO THRESHOLD
10,481.99 units
moodycamel/mpmc_unbounded/4p4c📈 view plot
⚠️ NO THRESHOLD
14,439.63 units
nim_channel/mpsc/1p1c📈 view plot
⚠️ NO THRESHOLD
2,581.11 units
nim_channel/mpsc/2p1c📈 view plot
⚠️ NO THRESHOLD
4,859.62 units
nim_channel/mpsc/4p1c📈 view plot
⚠️ NO THRESHOLD
5,105.56 units
nim_channels/mpmc/1p1c📈 view plot
⚠️ NO THRESHOLD
2,578.74 units
nim_channels/mpmc/1p2c📈 view plot
⚠️ NO THRESHOLD
1,214.39 units
nim_channels/mpmc/1p4c📈 view plot
⚠️ NO THRESHOLD
285.38 units
nim_channels/mpmc/2p1c📈 view plot
⚠️ NO THRESHOLD
5,563.76 units
nim_channels/mpmc/2p2c📈 view plot
⚠️ NO THRESHOLD
1,410.90 units
nim_channels/mpmc/2p4c📈 view plot
⚠️ NO THRESHOLD
681.38 units
nim_channels/mpmc/4p1c📈 view plot
⚠️ NO THRESHOLD
5,199.02 units
nim_channels/mpmc/4p2c📈 view plot
⚠️ NO THRESHOLD
1,984.85 units
nim_channels/mpmc/4p4c📈 view plot
⚠️ NO THRESHOLD
1,636.66 units
🐰 View full continuous benchmarking report in Bencher

@elijahr elijahr force-pushed the feat/bench-rollup-pr3-comparison-mvp branch from eb67e2b to daf38c5 Compare May 2, 2026 02:32
elijahr added a commit that referenced this pull request May 2, 2026
merge_bmf.py rejects slugs that aren't <library>/<topology>/<P>p<C>c
(exactly 3 segments). PR #23's CI failed on the moodycamel slug:

  invalid slug shape 'moodycamel/ConcurrentQueue/mpmc_unbounded/1p1c'

The same 4-segment violations exist for loony and crossbeam_seg_queue
in bench_unbounded.nim, and the crossbeam_array_queue dispatch was
emitting a colliding "crossbeam_queue" lib name. Apply the canonical
lib slugs from the design doc so all dispatch sites yield 3 segments
after the runner appends "/<topology>/<shape>":

  loony/LoonyQueue        -> loony
  crossbeam_queue/SegQueue -> crossbeam_seg_queue
  crossbeam_queue (Array)  -> crossbeam_array_queue
  moodycamel/ConcurrentQueue -> moodycamel

The PR #22 worktree applies the loony and crossbeam fixes too;
git rebase will harmonize the duplicate edits.

The adapter `name` constants (e.g. "moodycamel/ConcurrentQueue[uint64]")
are human-readable echo strings, not BMF slugs, and stay as-is.
@elijahr elijahr force-pushed the feat/bench-rollup-pr4-comparison-expansion branch from 5e3d07b to 3531f0c Compare May 2, 2026 02:33
@elijahr elijahr force-pushed the feat/bench-rollup-pr3-comparison-mvp branch from daf38c5 to ac54727 Compare May 2, 2026 03:51
elijahr added a commit that referenced this pull request May 2, 2026
merge_bmf.py rejects slugs that aren't <library>/<topology>/<P>p<C>c
(exactly 3 segments). PR #23's CI failed on the moodycamel slug:

  invalid slug shape 'moodycamel/ConcurrentQueue/mpmc_unbounded/1p1c'

The same 4-segment violations exist for loony and crossbeam_seg_queue
in bench_unbounded.nim, and the crossbeam_array_queue dispatch was
emitting a colliding "crossbeam_queue" lib name. Apply the canonical
lib slugs from the design doc so all dispatch sites yield 3 segments
after the runner appends "/<topology>/<shape>":

  loony/LoonyQueue        -> loony
  crossbeam_queue/SegQueue -> crossbeam_seg_queue
  crossbeam_queue (Array)  -> crossbeam_array_queue
  moodycamel/ConcurrentQueue -> moodycamel

The PR #22 worktree applies the loony and crossbeam fixes too;
git rebase will harmonize the duplicate edits.

The adapter `name` constants (e.g. "moodycamel/ConcurrentQueue[uint64]")
are human-readable echo strings, not BMF slugs, and stay as-is.
@elijahr elijahr force-pushed the feat/bench-rollup-pr4-comparison-expansion branch from 3531f0c to 44d91df Compare May 2, 2026 03:51
elijahr added a commit that referenced this pull request May 2, 2026
merge_bmf.py rejects slugs that aren't <library>/<topology>/<P>p<C>c
(exactly 3 segments). PR #23's CI failed on the moodycamel slug:

  invalid slug shape 'moodycamel/ConcurrentQueue/mpmc_unbounded/1p1c'

The same 4-segment violations exist for loony and crossbeam_seg_queue
in bench_unbounded.nim, and the crossbeam_array_queue dispatch was
emitting a colliding "crossbeam_queue" lib name. Apply the canonical
lib slugs from the design doc so all dispatch sites yield 3 segments
after the runner appends "/<topology>/<shape>":

  loony/LoonyQueue        -> loony
  crossbeam_queue/SegQueue -> crossbeam_seg_queue
  crossbeam_queue (Array)  -> crossbeam_array_queue
  moodycamel/ConcurrentQueue -> moodycamel

The PR #22 worktree applies the loony and crossbeam fixes too;
git rebase will harmonize the duplicate edits.

The adapter `name` constants (e.g. "moodycamel/ConcurrentQueue[uint64]")
are human-readable echo strings, not BMF slugs, and stay as-is.
@elijahr elijahr force-pushed the feat/bench-rollup-pr4-comparison-expansion branch from 44d91df to 170f37f Compare May 2, 2026 04:08
@elijahr
Copy link
Copy Markdown
Owner Author

elijahr commented May 2, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request expands the benchmark suite by adding three new third-party adapters: MoodyCamel's concurrentqueue (MPMC unbounded), the threading package's Chan (MPMC bounded), and Nim's stdlib system.Channel (MPSC bounded). The changes include vendoring the MoodyCamel header with a C++ wrapper, adding smoke tests, and integrating these adapters into the benchmark binaries and CI workflows. Feedback focuses on maintaining naming consistency across the adapter suite, specifically suggesting the use of the make...Adapter and cleanup naming conventions for the new modules, and ensuring the name procedure correctly reflects the generic type parameter instead of hardcoding a specific type.

PopResult[T](success: false)

proc name*[T](a: MoodycamelAdapter[T]): string =
"moodycamel/ConcurrentQueue[uint64]"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The name procedure hardcodes uint64 in the returned string, which is misleading since the adapter is generic over T. While the underlying C++ wrapper is specialized for uint64_t, the adapter's name should reflect the type parameter T for consistency with other adapters in the suite (e.g., NimChannelAdapter and ThreadingChannelsAdapter).

    "moodycamel/ConcurrentQueue[" & $T & "]"

Comment on lines +56 to +57
proc initNimChannelAdapter*[T](capacity: int = 1024
): NimChannelAdapter[T] =
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To maintain consistency with the existing adapters in the benchmark suite (e.g., makeLoonyAdapter, makeBoostLockfreeQueueAdapter, makeMoodycamelAdapter), it is recommended to rename this procedure to makeNimChannelAdapter.

  proc makeNimChannelAdapter*[T](capacity: int = 1024
  ): NimChannelAdapter[T] =

result.chan = create(Channel[T])
result.chan[].open(if capacity < 0: 0 else: capacity)

proc deinitNimChannelAdapter*[T](a: var NimChannelAdapter[T]) =
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The established pattern for adapter teardown in this repository is to use a procedure named cleanup. Renaming deinitNimChannelAdapter to cleanup will align this adapter with the others (Loony, Boost, Crossbeam, MoodyCamel) and simplify the harness logic.

  proc cleanup*[T](a: var NimChannelAdapter[T]) =

Comment on lines +48 to +49
proc initThreadingChannelsAdapter*[T](capacity: int = 1024
): ThreadingChannelsAdapter[T] =
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

For consistency with the make...Adapter naming convention used throughout the benchmark suite, consider renaming this procedure to makeThreadingChannelsAdapter.

  proc makeThreadingChannelsAdapter*[T](capacity: int = 1024
  ): ThreadingChannelsAdapter[T] =

Comment on lines +57 to +58
proc deinitThreadingChannelsAdapter*[T](
a: var ThreadingChannelsAdapter[T]) =
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The benchmark harness and other adapters in this project use the name cleanup for resource teardown. Renaming deinitThreadingChannelsAdapter to cleanup ensures consistency across the comparison set.

  proc cleanup*[T](
      a: var ThreadingChannelsAdapter[T]) =

Comment thread benchmarks/nim/bench_mpmc.nim Outdated

when defined(adapter_threading_channels_available):
proc initThreadingChannelsQ(capacity: int): ThreadingChannelsAdapter[uint64] =
initThreadingChannelsAdapter[uint64](capacity)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Update the call to match the suggested renaming of the adapter's initialization procedure.

    makeThreadingChannelsAdapter[uint64](capacity)

Comment thread benchmarks/nim/bench_mpsc.nim Outdated

when defined(adapter_nim_channel_available):
proc initNimChannelQ(capacity: int): NimChannelAdapter[uint64] =
initNimChannelAdapter[uint64](capacity)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Update the call to match the suggested renaming of the adapter's initialization procedure.

    makeNimChannelAdapter[uint64](capacity)

Comment thread tests/t_bench_adapters.nim Outdated
Comment on lines +126 to +127
initThreadingChannelsAdapter[uint64](capacity = 4096),
deinitThreadingChannelsAdapter(adapter)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Update the test case to use the consistent make... and cleanup procedure names.

        makeThreadingChannelsAdapter[uint64](capacity = 4096),
        cleanup(adapter)

Comment thread tests/t_bench_adapters.nim Outdated
Comment on lines +137 to +138
initNimChannelAdapter[uint64](capacity = 4096),
deinitNimChannelAdapter(adapter)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Update the test case to use the consistent make... and cleanup procedure names.

        makeNimChannelAdapter[uint64](capacity = 4096),
        cleanup(adapter)

@elijahr elijahr force-pushed the feat/bench-rollup-pr3-comparison-mvp branch from aa6dd1f to fd5b154 Compare May 2, 2026 06:02
elijahr added a commit that referenced this pull request May 2, 2026
merge_bmf.py rejects slugs that aren't <library>/<topology>/<P>p<C>c
(exactly 3 segments). PR #23's CI failed on the moodycamel slug:

  invalid slug shape 'moodycamel/ConcurrentQueue/mpmc_unbounded/1p1c'

The same 4-segment violations exist for loony and crossbeam_seg_queue
in bench_unbounded.nim, and the crossbeam_array_queue dispatch was
emitting a colliding "crossbeam_queue" lib name. Apply the canonical
lib slugs from the design doc so all dispatch sites yield 3 segments
after the runner appends "/<topology>/<shape>":

  loony/LoonyQueue        -> loony
  crossbeam_queue/SegQueue -> crossbeam_seg_queue
  crossbeam_queue (Array)  -> crossbeam_array_queue
  moodycamel/ConcurrentQueue -> moodycamel

The PR #22 worktree applies the loony and crossbeam fixes too;
git rebase will harmonize the duplicate edits.

The adapter `name` constants (e.g. "moodycamel/ConcurrentQueue[uint64]")
are human-readable echo strings, not BMF slugs, and stay as-is.
elijahr added a commit that referenced this pull request May 2, 2026
…el name

Per Gemini review on PR #23.

1. nim_channel_adapter: rename initNimChannelAdapter → makeNimChannelAdapter
   and deinitNimChannelAdapter → cleanup, matching the
   makeLoonyAdapter / makeBoostLockfreeQueueAdapter / makeMoodycamelAdapter
   / makeCrossbeamArrayQueueAdapter convention.

2. threading_channels_adapter: same rename — initThreadingChannelsAdapter
   → makeThreadingChannelsAdapter, deinitThreadingChannelsAdapter →
   cleanup. Update the docstring's example accordingly.

3. moodycamel_adapter.name: drop the hardcoded `uint64` literal in the
   returned string and use $T. The C++ wrapper is uint64-specialized
   today, but the adapter is generic; keeping the name in sync with T
   matches every other adapter's name proc.

4. Update three call-site groups: bench_mpmc.nim's initThreadingChannelsQ
   wrapper, bench_mpsc.nim's initNimChannelQ wrapper,
   smoke_threading_channels.nim, and t_bench_adapters.nim suite blocks.

Compile-checked bench_mpsc and bench_mpmc locally.
@elijahr elijahr force-pushed the feat/bench-rollup-pr4-comparison-expansion branch from 170f37f to 6b86349 Compare May 2, 2026 06:06
@elijahr
Copy link
Copy Markdown
Owner Author

elijahr commented May 2, 2026

/gemini review

@gemini-code-assist
Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@elijahr
Copy link
Copy Markdown
Owner Author

elijahr commented May 2, 2026

/review

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 2, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0)

Grey Divider


Remediation recommended

1. FFI exception escapes C ABI 🐞 Bug ☼ Reliability
Description
mc_init is an extern "C" function but uses throwing new without catching, so an allocation
failure can terminate/UB across the C ABI boundary. The Nim adapter also doesn’t fail fast if
initialization fails, which can turn init failure into hard-to-debug benchmark hangs/crashes.
Code

benchmarks/vendor/concurrentqueue/moodycamel_wrapper.cpp[R41-47]

+mc_queue_t mc_init(unsigned long long initial_capacity) {
+  std::size_t capacity = static_cast<std::size_t>(initial_capacity);
+  if (capacity == 0) {
+    capacity = 32;
+  }
+  return new moodycamel::ConcurrentQueue<std::uint64_t>(capacity);
+}
Evidence
The wrapper exposes a C ABI (extern "C") and constructs the queue via plain new with no error
handling, meaning any exception escapes the C boundary. The Nim adapter calls mc_init and stores
the returned pointer without validating it, and the throughput harness producer loop spins until
push succeeds, so a failed init can manifest as termination or non-progress depending on how
failure presents.

benchmarks/vendor/concurrentqueue/moodycamel_wrapper.cpp[29-47]
benchmarks/nim/adapters/moodycamel_adapter.nim[45-77]
benchmarks/nim/bench_common.nim[465-472]
Best Practice: C++ Core Guidelines / FFI best practice

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`mc_init` is an `extern "C"` function but uses throwing `new`. If allocation fails, the exception would escape across the C ABI boundary (terminate/UB). The Nim adapter also doesn’t validate the handle from `mc_init`, so init failure can become an opaque crash/hang.

### Issue Context
- `mc_init` is the only place that constructs the queue.
- Bench harness producer threads spin until `push` succeeds, so a non-functional queue handle can cause non-progress.

### Fix Focus Areas
- benchmarks/vendor/concurrentqueue/moodycamel_wrapper.cpp[41-47]
- benchmarks/nim/adapters/moodycamel_adapter.nim[57-63]

### Suggested fix
1. In `mc_init`, prevent exceptions from escaping by either:
  - using `new (std::nothrow)` and returning `nullptr` on failure, **or**
  - wrapping construction in `try { ... } catch (...) { return nullptr; }`.
2. In `makeMoodycamelAdapter`, validate `result.queue != nil` and fail fast (e.g., `raise`/`doAssert`) with a clear message so failures don’t devolve into benchmark hangs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@elijahr elijahr force-pushed the feat/bench-rollup-pr3-comparison-mvp branch from fd5b154 to 4ce6826 Compare May 2, 2026 10:33
elijahr added a commit that referenced this pull request May 2, 2026
merge_bmf.py rejects slugs that aren't <library>/<topology>/<P>p<C>c
(exactly 3 segments). PR #23's CI failed on the moodycamel slug:

  invalid slug shape 'moodycamel/ConcurrentQueue/mpmc_unbounded/1p1c'

The same 4-segment violations exist for loony and crossbeam_seg_queue
in bench_unbounded.nim, and the crossbeam_array_queue dispatch was
emitting a colliding "crossbeam_queue" lib name. Apply the canonical
lib slugs from the design doc so all dispatch sites yield 3 segments
after the runner appends "/<topology>/<shape>":

  loony/LoonyQueue        -> loony
  crossbeam_queue/SegQueue -> crossbeam_seg_queue
  crossbeam_queue (Array)  -> crossbeam_array_queue
  moodycamel/ConcurrentQueue -> moodycamel

The PR #22 worktree applies the loony and crossbeam fixes too;
git rebase will harmonize the duplicate edits.

The adapter `name` constants (e.g. "moodycamel/ConcurrentQueue[uint64]")
are human-readable echo strings, not BMF slugs, and stay as-is.
elijahr added a commit that referenced this pull request May 2, 2026
…el name

Per Gemini review on PR #23.

1. nim_channel_adapter: rename initNimChannelAdapter → makeNimChannelAdapter
   and deinitNimChannelAdapter → cleanup, matching the
   makeLoonyAdapter / makeBoostLockfreeQueueAdapter / makeMoodycamelAdapter
   / makeCrossbeamArrayQueueAdapter convention.

2. threading_channels_adapter: same rename — initThreadingChannelsAdapter
   → makeThreadingChannelsAdapter, deinitThreadingChannelsAdapter →
   cleanup. Update the docstring's example accordingly.

3. moodycamel_adapter.name: drop the hardcoded `uint64` literal in the
   returned string and use $T. The C++ wrapper is uint64-specialized
   today, but the adapter is generic; keeping the name in sync with T
   matches every other adapter's name proc.

4. Update three call-site groups: bench_mpmc.nim's initThreadingChannelsQ
   wrapper, bench_mpsc.nim's initNimChannelQ wrapper,
   smoke_threading_channels.nim, and t_bench_adapters.nim suite blocks.

Compile-checked bench_mpsc and bench_mpmc locally.
@elijahr elijahr force-pushed the feat/bench-rollup-pr4-comparison-expansion branch from 6b86349 to 042f763 Compare May 2, 2026 10:34
elijahr added a commit that referenced this pull request May 3, 2026
merge_bmf.py rejects slugs that aren't <library>/<topology>/<P>p<C>c
(exactly 3 segments). PR #23's CI failed on the moodycamel slug:

  invalid slug shape 'moodycamel/ConcurrentQueue/mpmc_unbounded/1p1c'

The same 4-segment violations exist for loony and crossbeam_seg_queue
in bench_unbounded.nim, and the crossbeam_array_queue dispatch was
emitting a colliding "crossbeam_queue" lib name. Apply the canonical
lib slugs from the design doc so all dispatch sites yield 3 segments
after the runner appends "/<topology>/<shape>":

  loony/LoonyQueue        -> loony
  crossbeam_queue/SegQueue -> crossbeam_seg_queue
  crossbeam_queue (Array)  -> crossbeam_array_queue
  moodycamel/ConcurrentQueue -> moodycamel

The PR #22 worktree applies the loony and crossbeam fixes too;
git rebase will harmonize the duplicate edits.

The adapter `name` constants (e.g. "moodycamel/ConcurrentQueue[uint64]")
are human-readable echo strings, not BMF slugs, and stay as-is.
elijahr added a commit that referenced this pull request May 3, 2026
…el name

Per Gemini review on PR #23.

1. nim_channel_adapter: rename initNimChannelAdapter → makeNimChannelAdapter
   and deinitNimChannelAdapter → cleanup, matching the
   makeLoonyAdapter / makeBoostLockfreeQueueAdapter / makeMoodycamelAdapter
   / makeCrossbeamArrayQueueAdapter convention.

2. threading_channels_adapter: same rename — initThreadingChannelsAdapter
   → makeThreadingChannelsAdapter, deinitThreadingChannelsAdapter →
   cleanup. Update the docstring's example accordingly.

3. moodycamel_adapter.name: drop the hardcoded `uint64` literal in the
   returned string and use $T. The C++ wrapper is uint64-specialized
   today, but the adapter is generic; keeping the name in sync with T
   matches every other adapter's name proc.

4. Update three call-site groups: bench_mpmc.nim's initThreadingChannelsQ
   wrapper, bench_mpsc.nim's initNimChannelQ wrapper,
   smoke_threading_channels.nim, and t_bench_adapters.nim suite blocks.

Compile-checked bench_mpsc and bench_mpmc locally.
@elijahr elijahr force-pushed the feat/bench-rollup-pr4-comparison-expansion branch from d55a6b6 to f184183 Compare May 3, 2026 03:05
@elijahr
Copy link
Copy Markdown
Owner Author

elijahr commented May 3, 2026

@gemini-code-assist review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request expands the benchmark suite by integrating three new third-party adapters: MoodyCamel's concurrentqueue (which is now vendored), the Nimble threading package, and Nim's standard system.Channel. The changes include the adapter implementations, corresponding smoke tests, CI workflow enhancements, and updated documentation. Review feedback highlights the need for a null check after memory allocation in the Nim channel adapter to prevent potential crashes and suggests strengthening static assertions in the MoodyCamel adapter to exclude managed types like strings and sequences, ensuring memory safety across the C++ boundary.

Comment on lines +62 to +63
result.chan = create(Channel[T])
result.chan[].open(if capacity < 0: 0 else: capacity)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

create(Channel[T]) (which uses alloc0) can return nil if the system is out of memory. Dereferencing it immediately with result.chan[].open(...) will cause a crash. Adding a check for nil and raising an OutOfMemDefect ensures a cleaner failure mode, consistent with the error handling in the MoodyCamel adapter.

    result.chan = create(Channel[T])
    if result.chan == nil:
      raise newException(OutOfMemDefect, "Failed to allocate memory for system.Channel")
    result.chan[].open(if capacity < 0: 0 else: capacity)

Comment on lines +71 to +74
assert not (T is ref),
"MoodycamelAdapter cannot transport ref types: the C++ queue " &
"bypasses Nim's GC, so refcounts wouldn't be maintained across " &
"the boundary. Use a non-ref 64-bit payload (uint64, ptr, etc)."
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The static assertion only checks for ref types, but other managed types like string and seq are also problematic when passed through an opaque C++ queue. Since the C++ implementation bypasses Nim's GC/ARC, reference counts for these types won't be maintained, leading to memory leaks or premature deallocation. It's safer to explicitly exclude them.

      assert not (T is (ref | string | seq)),
        "MoodycamelAdapter cannot transport managed types (ref, string, seq): " &
        "the C++ queue bypasses Nim's GC, so memory wouldn't be maintained " &
        "across the boundary. Use a non-managed 64-bit payload (uint64, ptr, etc)."

@elijahr elijahr force-pushed the feat/bench-rollup-pr3-comparison-mvp branch from f76b84c to b233388 Compare May 3, 2026 03:41
elijahr added a commit that referenced this pull request May 3, 2026
merge_bmf.py rejects slugs that aren't <library>/<topology>/<P>p<C>c
(exactly 3 segments). PR #23's CI failed on the moodycamel slug:

  invalid slug shape 'moodycamel/ConcurrentQueue/mpmc_unbounded/1p1c'

The same 4-segment violations exist for loony and crossbeam_seg_queue
in bench_unbounded.nim, and the crossbeam_array_queue dispatch was
emitting a colliding "crossbeam_queue" lib name. Apply the canonical
lib slugs from the design doc so all dispatch sites yield 3 segments
after the runner appends "/<topology>/<shape>":

  loony/LoonyQueue        -> loony
  crossbeam_queue/SegQueue -> crossbeam_seg_queue
  crossbeam_queue (Array)  -> crossbeam_array_queue
  moodycamel/ConcurrentQueue -> moodycamel

The PR #22 worktree applies the loony and crossbeam fixes too;
git rebase will harmonize the duplicate edits.

The adapter `name` constants (e.g. "moodycamel/ConcurrentQueue[uint64]")
are human-readable echo strings, not BMF slugs, and stay as-is.
elijahr added a commit that referenced this pull request May 3, 2026
…el name

Per Gemini review on PR #23.

1. nim_channel_adapter: rename initNimChannelAdapter → makeNimChannelAdapter
   and deinitNimChannelAdapter → cleanup, matching the
   makeLoonyAdapter / makeBoostLockfreeQueueAdapter / makeMoodycamelAdapter
   / makeCrossbeamArrayQueueAdapter convention.

2. threading_channels_adapter: same rename — initThreadingChannelsAdapter
   → makeThreadingChannelsAdapter, deinitThreadingChannelsAdapter →
   cleanup. Update the docstring's example accordingly.

3. moodycamel_adapter.name: drop the hardcoded `uint64` literal in the
   returned string and use $T. The C++ wrapper is uint64-specialized
   today, but the adapter is generic; keeping the name in sync with T
   matches every other adapter's name proc.

4. Update three call-site groups: bench_mpmc.nim's initThreadingChannelsQ
   wrapper, bench_mpsc.nim's initNimChannelQ wrapper,
   smoke_threading_channels.nim, and t_bench_adapters.nim suite blocks.

Compile-checked bench_mpsc and bench_mpmc locally.
@elijahr elijahr force-pushed the feat/bench-rollup-pr4-comparison-expansion branch from f184183 to d1ac052 Compare May 3, 2026 03:41
@elijahr
Copy link
Copy Markdown
Owner Author

elijahr commented May 4, 2026

/ai-review

@axiomantic-momus
Copy link
Copy Markdown

axiomantic-momus Bot commented May 4, 2026

Momus review posted — verdict APPROVE, 1 finding

████████████████████ 100%

run log

@elijahr
Copy link
Copy Markdown
Owner Author

elijahr commented May 4, 2026

/ai-review

2 similar comments
@elijahr
Copy link
Copy Markdown
Owner Author

elijahr commented May 4, 2026

/ai-review

@elijahr
Copy link
Copy Markdown
Owner Author

elijahr commented May 5, 2026

/ai-review

Copy link
Copy Markdown

@axiomantic-momus axiomantic-momus Bot left a comment

Choose a reason for hiding this comment

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

PR adds three new queue adapters (MoodyCamel, threading.Chan, system.Channel) with CI soft-skip wiring, smoke tests, and bench-binary dispatch. The code is well-crafted, handles edge cases (null queue, failed init, cross-ABI exceptions, 32-bit capacity clamping), and follows existing adapter conventions. Test coverage exercises basic push/pop round-trips but — consistently with prior adapters — no concurrent multi-threaded unit tests. No blocking issues found.

Severity tally: 1 Low.

Low

  • BOT-A1 (benchmarks/nim/adapters/threading_channels_adapter.nim:20): Misleading docstring: references non-existent deinit proc

Noteworthy

  • MoodyCamel wrapper correctly handles cross-ABI exceptions by catching all C++ exceptions and returning nullptr, preventing undefined behavior at the C ABI boundary.
  • Capacity edging in moodycamel_wrapper.cpp handles 32-bit SIZE_MAX clamping, avoiding wrap-around on ILP32 targets.
  • Compile-time assertions in moodycamel_adapter.nim (sizeof(T)==8, T not ref) catch misuse at build time rather than at runtime.

Verdict: APPROVE.

Commands
  • Comment /ai-review or mention @axiomantic-momus[bot] to request a re-review of the latest changes.
  • Reply to a finding with won't fix, by design, or not a bug to decline it.
  • Reply with instead, ... to propose an alternative fix.

Powered by Momus running deepseek/deepseek-v4-pro via openrouter.ai.

Comment on lines +20 to +24
## Memory management: ``Chan[T]`` is a reference-counted object that
## owns its underlying buffer. We hold it as a value field on the
## adapter so the destructor runs automatically when the adapter goes
## out of scope; the optional ``deinit`` proc explicitly tears down a
## migrated value before the adapter is dropped.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

BOT-A1 — Low (quality)
Misleading docstring: references non-existent deinit proc
The module-level Memory management section states 'the optional deinit proc explicitly tears down a migrated value before the adapter is dropped'. The adapter defines no deinit proc — only a cleanup proc that is explicitly a no-op. The cleanup proc's own docstring correctly explains that Chan[T] self-cleans via its hooks. The module-level comment likely intended to describe Nim's general =destroy mechanism but reads as if the adapter itself provides a deinit procedure.

@elijahr elijahr force-pushed the feat/bench-rollup-pr3-comparison-mvp branch from b233388 to 41a08f3 Compare May 5, 2026 03:50
elijahr added 10 commits May 4, 2026 22:51
- benchmarks/vendor/concurrentqueue/{concurrentqueue.h,LICENSE.md,README.md,moodycamel_wrapper.cpp}: vendor MoodyCamel ConcurrentQueue at upstream commit d655418bb644b7f85159d94c591d7d983949fb81 (BSD-2-Clause / Boost dual). The single-header library is shimmed by an extern "C" wrapper exposing mc_init / mc_push / mc_pop / mc_destroy for uint64_t so the Nim adapter consumes plain importc, not the upstream template machinery.
- benchmarks/nim/adapters/moodycamel_adapter.nim: nim cpp adapter, topologiesSupported = {tMpmcUnbounded}, gated on -d:adapter_moodycamel_available.
- benchmarks/nim/adapters/threading_channels_adapter.nim: nimble threading.Chan[T] adapter, topologiesSupported = {tMpmc}, non-blocking trySend/tryRecv, gated on -d:adapter_threading_channels_available.
- benchmarks/nim/adapters/nim_channel_adapter.nim: stdlib system.Channel[T] adapter, topologiesSupported = {tMpsc}, blocking-on-full producer / non-blocking consumer (apples-to-oranges fairness caveat documented inline), gated on -d:adapter_nim_channel_available.
- benchmarks/nim/smoke/smoke_moodycamel.nim, smoke_threading_channels.nim: round-trip smokes for the new adapters.
- tests/t_bench_adapters.nim: extend with the three new gates.
- bench_mpsc.nim: when declared(initNimChannelQ) -> nim_channel/mpsc/<P>p1c slugs (P in {1,2,4}). Uses runThroughputHarness; producer side blocks on full per system.Channel semantics (fairness caveat documented in adapter file).
- bench_mpmc.nim: when declared(initThreadingChannelsQ) -> threading_channels/mpmc/<P>p<C>c slugs ({1,2,4}x{1,2,4}). Uses runThroughputHarness; non-blocking trySend / tryRecv.
- bench_unbounded.nim: when declared(initMoodycamelQ) -> moodycamel/ConcurrentQueue/mpmc_unbounded/<P>p<C>c slugs ({1,2,4}x{1,2,4}). Uses runThroughputHarness; vendored single-header + extern "C" shim, nim cpp only.

Verified by per-variant smoke runs at 1000 messages: each variant emits the full expected slug set. Default builds without any gate continue to compile clean and emit the existing pre-PR-4 slug set.
- workflow_dispatch inputs: force_skip_moodycamel, force_skip_threading_channels, force_skip_nim_channel.
- env vars: FORCE_SKIP_MOODYCAMEL / FORCE_SKIP_THREADING_CHANNELS / FORCE_SKIP_NIM_CHANNEL gate the per-adapter steps.
- MoodyCamel (bench_unbounded only): vendored-header presence check + nim cpp smoke; requires nim cpp -> sets NIM_MODE=cpp via ADAPTER_MOODYCAMEL.
- threading.Chan (bench_mpmc only): nimble install threading + smoke; nim c.
- system.Channel (bench_mpsc only): no install (stdlib); smoke via t_bench_adapters.nim under -d:adapter_nim_channel_available.
- bench_mpsc compile step now consumes ADAPTER_FLAGS so nim_channel actually wires in (previously left blank because there was no MVP adapter for MPSC).
- bench_unbounded compile step switches from hard-coded `nim c` to $NIM_MODE so MoodyCamel can request `nim cpp` while loony continues to be mode-neutral.

actionlint clean.
- THIRD_PARTY_LICENSES.md: drop the 'Future entries (PR 4 / PR 5)' placeholder for concurrentqueue; add the real Comparison-expansion section with concrete blocks for concurrentqueue (vendored at d655418bb644b7f85159d94c591d7d983949fb81, BSD/Boost dual), nimble threading (MIT, unvendored), and Nim system.Channel (MIT, stdlib).
- .gitattributes: new file with benchmarks/vendor/** linguist-vendored=true linguist-generated=true so the vendored MoodyCamel header doesn't skew GitHub language stats. Verified via git check-attr.
- benchmarks/README.md: rename 'Comparison MVP — third-party adapters' to 'Comparison libraries' and extend the table from 5 to 8 rows (Loony, Boost x2, Crossbeam x2, MoodyCamel, threading.Chan, system.Channel). Add per-adapter local-run examples for the 3 new adapters. Add the apples-to-oranges fairness asterisk for system.Channel's blocking-on-full producer.
- CHANGELOG.md: prepend the PR-4 / Track-4 'Added' bullets to [Unreleased] covering the four new adapters, vendored MoodyCamel, .gitattributes rule, three new force_skip flags, t_bench_adapters extension, and bench binary slug coverage.
merge_bmf.py rejects slugs that aren't <library>/<topology>/<P>p<C>c
(exactly 3 segments). PR #23's CI failed on the moodycamel slug:

  invalid slug shape 'moodycamel/ConcurrentQueue/mpmc_unbounded/1p1c'

The same 4-segment violations exist for loony and crossbeam_seg_queue
in bench_unbounded.nim, and the crossbeam_array_queue dispatch was
emitting a colliding "crossbeam_queue" lib name. Apply the canonical
lib slugs from the design doc so all dispatch sites yield 3 segments
after the runner appends "/<topology>/<shape>":

  loony/LoonyQueue        -> loony
  crossbeam_queue/SegQueue -> crossbeam_seg_queue
  crossbeam_queue (Array)  -> crossbeam_array_queue
  moodycamel/ConcurrentQueue -> moodycamel

The PR #22 worktree applies the loony and crossbeam fixes too;
git rebase will harmonize the duplicate edits.

The adapter `name` constants (e.g. "moodycamel/ConcurrentQueue[uint64]")
are human-readable echo strings, not BMF slugs, and stay as-is.
…el name

Per Gemini review on PR #23.

1. nim_channel_adapter: rename initNimChannelAdapter → makeNimChannelAdapter
   and deinitNimChannelAdapter → cleanup, matching the
   makeLoonyAdapter / makeBoostLockfreeQueueAdapter / makeMoodycamelAdapter
   / makeCrossbeamArrayQueueAdapter convention.

2. threading_channels_adapter: same rename — initThreadingChannelsAdapter
   → makeThreadingChannelsAdapter, deinitThreadingChannelsAdapter →
   cleanup. Update the docstring's example accordingly.

3. moodycamel_adapter.name: drop the hardcoded `uint64` literal in the
   returned string and use $T. The C++ wrapper is uint64-specialized
   today, but the adapter is generic; keeping the name in sync with T
   matches every other adapter's name proc.

4. Update three call-site groups: bench_mpmc.nim's initThreadingChannelsQ
   wrapper, bench_mpsc.nim's initNimChannelQ wrapper,
   smoke_threading_channels.nim, and t_bench_adapters.nim suite blocks.

Compile-checked bench_mpsc and bench_mpmc locally.
mc_init was an extern "C" function that constructed the queue with
plain `new`, so an allocation failure (or any other constructor
exception) would propagate across the C ABI boundary — undefined
behavior. Switch to `new (std::nothrow)` and wrap a try/catch around
the construction so init failure becomes a clean nullptr return.

The Nim adapter's makeMoodycamelAdapter now validates the returned
handle and raises OutOfMemDefect when mc_init returns nullptr. Without
this check a failed init would manifest as the producer thread
spin-retrying `push` with prFull (queue == nil branch) forever,
turning init failure into an opaque benchmark hang.
moodycamel_adapter.nim:
  - push/pop now use cast[uint64](item) / cast[T](uint64(raw)) instead
    of value conversions. For T=uint64 this is identical, but it lets
    the adapter round-trip non-numeric 64-bit payloads (pointers, refs,
    distinct-int aliases) by their bit pattern.

moodycamel_wrapper.cpp:
  - Clamp the uint64 capacity hint to SIZE_MAX before casting so on
    32-bit hosts (size_t == uint32) a >4 GiB value doesn't truncate to
    a tiny number. Bench CI is x86_64 and never exercises the clamp,
    but the wrapper is exposed via the public concurrentqueue header
    so the safer cast is worth carrying. Add <cstddef> for SIZE_MAX.
The C++ wrapper is hardcoded to uint64_t. The push/pop sites use
cast[uint64](item) and cast[T](uint64(raw)) to round-trip the bit
pattern, but those casts only behave when T is exactly 8 bytes. Add
a static assertion in makeMoodycamelAdapter to fail fast at the
instantiation site rather than corrupting state at run time.

Also reject T=ref: the C++ queue is opaque to Nim's GC, so a refcount
bump on push would never happen and the underlying object could be
collected before pop. Restrict to integer / ptr-like 64-bit payloads.
…it proc

The adapter never exposes a deinit proc; cleanup() is the no-op shape-
parity stub and =destroy on Chan[T] does the actual buffer reclamation.
@elijahr elijahr force-pushed the feat/bench-rollup-pr4-comparison-expansion branch from d1ac052 to 9b3e9f2 Compare May 5, 2026 03:51
@elijahr
Copy link
Copy Markdown
Owner Author

elijahr commented May 5, 2026

Consolidated into #19 — fast-forwarded feat/bench-rollup-pr0-bench-common to this branch's tip; all commits preserved in #19. Closing this stacked PR as redundant.

@elijahr elijahr closed this May 5, 2026
@elijahr elijahr deleted the feat/bench-rollup-pr4-comparison-expansion branch May 5, 2026 04:19
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