Skip to content

perf: reduce pre-allocations, default to single-thread runtime - #556

Closed
jlucaso1 wants to merge 4 commits into
mainfrom
perf/reduce-over-allocations
Closed

jlucaso1 wants to merge 4 commits into
mainfrom
perf/reduce-over-allocations

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Apr 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Reduce over-sized cache/channel pre-allocations and default to single-thread Tokio runtime for lower memory usage.

DHAT impact (multi-thread, capacity reductions only):

Metric Before After Delta
DHAT total bytes 26.0 MB 25.3 MB -682 KB (-2.6%)

Single-thread runtime impact (on top of capacity reductions):

Metric Multi-thread Single-thread Delta
DHAT total bytes 25.3 MB 24.7 MB -580 KB (-2.3%)
DHAT peak live 5.10 MB 4.61 MB -490 KB (-9.6%)

Full optimization journey (all PRs combined):

Metric Original Now Delta
DHAT total bytes 37.9 MB 24.7 MB -13.1 MB (-34.7%)
DHAT total blocks 158K 84K -74K (-47.1%)

Changes

1. Reduce cache/channel pre-allocations

All values remain configurable via CacheConfig.

Resource Before After
Transport event channel 1,024 64
Send job channel 32 8
Signal cache max entries 10,000 2,000
session_locks 10,000 2,000
chat_lanes 5,000 1,000
device_registry 5,000 1,000
lid_pn_cache 10,000 2,000
retried_group_messages 2,000 500
message_retry_counts 1,000 500
pdo_pending_requests 500 200
enc_buf initial 4,096 1,024

2. Default to single-thread runtime

  • Removed tokio-native from default features
  • Single-thread handles ~3K msg/sec sustained (sufficient for typical bot usage)
  • Matches WA Web's single-thread execution model
  • Saves ~500 KB peak memory from eliminated scheduler overhead
  • High-throughput deployments: add features = ["tokio-native"] for multi-thread (5K+ msg/sec)

3. Auto-detect yield frequency

  • TokioRuntime::yield_frequency() uses Handle::current().runtime_flavor()
  • Single-thread: yields every frame (frequency 1) to avoid event loop starvation
  • Multi-thread: yields every 10 frames to minimize overhead
  • Works correctly for both modes without configuration

Cleanup

  • Removed dead new_pdo_cache() function (cache built from CacheConfig, never called)
  • Fixed stale comment on send job channel capacity

Benchmark validation

  • 50K messages at 5K msg/sec (multi-thread): zero drops, 117ms avg latency
  • 30K messages at 3K msg/sec (single-thread): zero drops, 44ms avg latency

Test plan

  • cargo test --all (874 tests pass)
  • cargo clippy --all --tests clean
  • DHAT profiling verified
  • Throughput benchmark: multi-thread 5K/sec, single-thread 3K/sec

Reduce default capacities that were sized for extreme scenarios but
waste memory in typical single-client usage:

- Transport event channel: keep at 64 (user-tested at 5K msg/sec)
- NoiseSocket send job channel: 32 -> 8 (serialized sender, <1ms/job)
- Signal cache max entries: 10,000 -> 2,000 (typical ~200 contacts)
- session_locks: 10,000 -> 2,000
- chat_lanes: 5,000 -> 1,000
- device_registry: 5,000 -> 1,000
- lid_pn_cache: 10,000 -> 2,000
- retried_group_messages: 2,000 -> 500
- message_retry_counts: 1,000 -> 500
- pdo_pending_requests: 500 -> 200
- enc_buf initial capacity: 4,096 -> 1,024

All capacities are still configurable via CacheConfig. Moka caches
grow lazily so these are eviction ceilings, not pre-allocations.
The bounded channels (transport, send_job) do pre-allocate ring buffers.
@coderabbitai

coderabbitai Bot commented Apr 15, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Reduced default cache capacities and channel/backpressure sizes, removed a PDO cache helper, switched some Tokio runtimes to current-thread, and made yield_now() return an actual async yield future.

Changes

Cohort / File(s) Summary
Cache configuration & signal cache
src/cache_config.rs, wacore/src/store/signal_cache.rs
Lowered multiple default capacities: device_registry_cache 5_000→1_000, lid_pn_cache 10_000→2_000, retried_group_messages 2_000→500, message_retry_counts 1_000→500, pdo_pending_requests 500→200, session_locks_capacity 10_000→2_000, chat_lanes_capacity 5_000→1_000; DEFAULT_MAX_CACHE_ENTRIES 10_000→2_000.
Socket & transport channels
src/socket/noise_socket.rs, transports/tokio-transport/src/lib.rs
Reduced bounded channel capacities: noise send-job channel 32→8 and websocket event channel 1_024→64; reduced noise socket encryption scratch buffer capacity 4096→1024 (framing out_buf unchanged).
Runtime & examples
src/runtime_impl.rs, examples/benchmark.rs, src/main.rs
Switched Tokio runtime builders from new_multi_thread() to new_current_thread() in example and main; Runtime::yield_now() now returns Some(Box::pin(tokio::task::yield_now())) and yield_frequency() added.
PDO cache removal
src/pdo.rs
Removed pub fn new_pdo_cache() -> Cache<ChatMessageId, PendingPdoRequest> and its use crate::cache::Cache; import.
Cargo features
Cargo.toml
Removed tokio-native from the crate default feature list.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I trimmed the caches, light on my feet,
Queues grow shorter — the burrow's neat.
Fewer crumbs in every tiny store,
Tasks now yield and hop once more.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: reducing pre-allocations and defaulting to single-thread runtime, which are the core themes across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/reduce-over-allocations

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/cache_config.rs`:
- Around line 174-175: The config field pdo_pending_requests (type
CacheEntryConfig) has its default capacity changed to 200 but the PDO cache
instantiation still hardcodes .max_capacity(500) in src/pdo.rs; update the PDO
cache creation to read the capacity from the pdo_pending_requests config instead
of using .max_capacity(500) (e.g., use the CacheEntryConfig.capacity/
max_capacity accessor on the pdo_pending_requests instance when calling the
cache builder), or alternatively align the default back to 500 so the hardcoded
value and config match; locate the cache construction code in pdo.rs that calls
.max_capacity(500) and replace it to use the pdo_pending_requests configuration
value.

In `@src/socket/noise_socket.rs`:
- Around line 47-49: The comment above the channel creation for send jobs
currently says a buffer size of 32 but the actual call uses
async_channel::bounded::<SendJob>(8); update the comment to state a buffer size
of 8 (or adjust the bound to match the documented 32) so the comment and the
channel creation for send_job_tx/send_job_rx (SendJob) are consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a1501357-5bda-450d-a732-05b32efa03f3

📥 Commits

Reviewing files that changed from the base of the PR and between 88a5145 and fd1ba33.

📒 Files selected for processing (4)
  • src/cache_config.rs
  • src/socket/noise_socket.rs
  • transports/tokio-transport/src/lib.rs
  • wacore/src/store/signal_cache.rs

Comment thread src/cache_config.rs
Comment on lines +174 to 175
/// PDO pending requests (time_to_live). Default: 30s TTL, 200 entries.
pub pdo_pending_requests: CacheEntryConfig,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

pdo_pending_requests default appears out of sync with runtime capacity.

Line 247 changes default capacity to 200, but src/pdo.rs (Line 39 in the provided snippet) still uses a hardcoded .max_capacity(500). That makes this config value ineffective for the actual PDO cache path.

Suggested follow-up patch (outside this file)
-// src/pdo.rs
-.max_capacity(500)
+// src/pdo.rs
+.max_capacity(cache_config.pdo_pending_requests.capacity.max(1))

Also applies to: 247-247

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/cache_config.rs` around lines 174 - 175, The config field
pdo_pending_requests (type CacheEntryConfig) has its default capacity changed to
200 but the PDO cache instantiation still hardcodes .max_capacity(500) in
src/pdo.rs; update the PDO cache creation to read the capacity from the
pdo_pending_requests config instead of using .max_capacity(500) (e.g., use the
CacheEntryConfig.capacity/ max_capacity accessor on the pdo_pending_requests
instance when calling the cache builder), or alternatively align the default
back to 500 so the hardcoded value and config match; locate the cache
construction code in pdo.rs that calls .max_capacity(500) and replace it to use
the pdo_pending_requests configuration value.

Comment thread src/socket/noise_socket.rs Outdated
@github-actions

github-actions Bot commented Apr 15, 2026

Copy link
Copy Markdown

Benchmark Results

59 unchanged benchmark(s)
Benchmark Current Baseline Change
reporting_token_benchmark::content_extraction_group::bench_content_extraction simple:setup_simple_message() 3,933 3,933 +0.0%
reporting_token_benchmark::content_extraction_group::bench_content_extraction extended:setup_extended_message() 12,038 12,038 +0.0%
reporting_token_benchmark::key_derivation_group::bench_key_derivation 43,414 43,414 +0.0%
reporting_token_benchmark::token_calculation_group::bench_token_calculation 19,365 19,365 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation simple:setup_full_gen_simple() 68,478 68,478 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation extended:setup_full_gen_extended() 76,578 76,578 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding simple:setup_simple_message() 2,230 2,230 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding extended:setup_extended_message() 5,988 5,988 +0.0%
send_receive_benchmark::dm_send::bench_dm_send text:setup_dm_send() 170,450 170,027 +0.2%
send_receive_benchmark::dm_recv::bench_dm_recv text:setup_dm_recv() 191,898 191,750 +0.1%
send_receive_benchmark::group_send::bench_group_send group_10:setup_group_send_10() 875,947 875,956 -0.0%
send_receive_benchmark::group_send::bench_group_send group_50:setup_group_send_50() 966,955 966,970 -0.0%
send_receive_benchmark::group_send::bench_group_send group_256:setup_group_send_256() 1,453,214 1,453,214 +0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_10:setup_group_skdm_10() 2,585,241 2,578,882 +0.2%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_50:setup_group_skdm_50() 9,388,174 9,422,733 -0.4%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_256:setup_group_skdm_256() 44,519,183 44,696,268 -0.4%
send_receive_benchmark::group_recv::bench_group_recv text:setup_group_recv() 12,681,496 12,679,567 +0.0%
binary_benchmark::marshal_group::bench_marshal_allocating 71,247 71,247 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_allocating 71,300 71,300 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_allocating 98,367 98,367 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer 78,801 78,801 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer_vec_writer 71,347 71,347 +0.0%
binary_benchmark::marshal_group::bench_marshal_long_string 7,518 7,518 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_long_string 7,561 7,561 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_long_string 9,273 9,273 +0.0%
binary_benchmark::marshal_group::bench_marshal_huge_bytes_allocating 530,504 530,504 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_huge_bytes_allocating 530,072 530,072 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_huge_bytes_allocating 531,427 531,427 +0.0%
binary_benchmark::marshal_group::bench_marshal_many_children_allocating 8,506,160 8,506,160 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_many_children_allocating 8,450,412 8,450,412 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_many_children_allocating 19,677,947 19,677,947 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal small:setup_small_marshaled() 2,468 2,468 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal large:setup_large_marshaled() 33,558 33,558 +0.0%
binary_benchmark::unpack_group::bench_unpack_uncompressed 787 787 +0.0%
binary_benchmark::unpack_group::bench_unpack_compressed 526,732 526,732 +0.0%
binary_benchmark::attr_parser_group::bench_attr_parser attr_lookup:setup_attr_marshaled() 4,986 4,986 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip small:setup_small_marshaled() 5,315 5,315 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip large:setup_large_marshaled() 61,874 61,874 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto small:setup_small_marshaled() 5,347 5,347 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto large:setup_large_marshaled() 61,942 61,942 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact small:setup_small_marshaled() 6,734 6,734 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact large:setup_large_marshaled() 85,564 85,564 +0.0%
binary_benchmark::child_iteration_group::bench_get_children_by_tag 477,570 477,570 +0.0%
binary_benchmark::jid_optimization_group::bench_jid_to_owned_access jid_access:setup_jid_heavy_marshaled() 11,563 11,563 +0.0%
libsignal_benchmark::dm_group::bench_dm_session_establishment setup:setup_dm_users() 17,182,970 17,396,593 -1.2%
libsignal_benchmark::dm_group::bench_dm_encrypt_first_message first_msg:setup_dm_session() 157,923 157,923 +0.0%
libsignal_benchmark::dm_group::bench_dm_decrypt_first_message decrypt_prekey:setup_dm_with_first_message() 5,511,084 5,511,084 +0.0%
libsignal_benchmark::dm_group::bench_dm_encrypt_subsequent_message subsequent:setup_established_dm_session() 158,737 158,737 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_create_distribution_message create:setup_group_sender() 296,767 296,767 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_encrypt_message encrypt:setup_group_with_distribution() 707,098 707,098 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_decrypt_message decrypt:setup_group_with_encrypted_message() 12,531,719 12,435,641 +0.8%
libsignal_benchmark::conversation_group::bench_full_dm_conversation full:setup_conversation_data() 27,620,146 27,451,016 +0.6%
libsignal_benchmark::signature_group::bench_signature_creation sign:setup_keypair_with_message() 3,467,011 3,467,011 +0.0%
libsignal_benchmark::signature_group::bench_signature_verification verify:setup_keypair_with_message() 127,247,353 125,070,503 +1.7%
libsignal_benchmark::signature_group::bench_key_generation keygen 2,830,452 2,830,452 +0.0%
libsignal_benchmark::session_optimization_group::bench_decrypt_with_previous_session previous_session:setup_with_archived_sessions() 46,003 46,003 +0.0%
libsignal_benchmark::session_optimization_group::bench_out_of_order_decryption out_of_order:setup_out_of_order_messages() 5,090,932 5,090,932 +0.0%
libsignal_benchmark::session_optimization_group::bench_promote_matching_session promote:setup_promote_matching_session() 316,987 316,987 +0.0%
libsignal_benchmark::session_optimization_group::bench_message_key_eviction eviction:setup_message_key_eviction() 14,254,317 14,254,317 +0.0%
No significant changes detected.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fd1ba333fb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cache_config.rs
session_locks_capacity: 10_000,
chat_lanes_capacity: 5_000,
session_locks_capacity: 2_000,
chat_lanes_capacity: 1_000,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Raise chat lane capacity to prevent duplicate per-chat workers

chat_lanes is a capacity-evicted coordination cache, and this default (1_000) is now low enough to hit in long-lived/offline-sync sessions with many distinct chats. When a lane is evicted while its old worker is still draining queued messages (MessageHandler::handle + create_chat_lane in src/handlers/message.rs), the next message for that chat creates a new lane/worker, so two workers process the same chat concurrently and message order guarantees can be violated. The comment immediately above this setting already documents that eviction can break serialization, so lowering this default introduces correctness risk rather than only a memory tradeoff.

Useful? React with 👍 / 👎.

- Comment said buffer size 32 but code uses 8 after reduction
- new_pdo_cache() was never called (client uses CacheConfig instead)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ed06f9c97c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cache_config.rs
// breaking serialization. Size generously to avoid eviction pressure.
session_locks_capacity: 10_000,
chat_lanes_capacity: 5_000,
session_locks_capacity: 2_000,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Increase session lock capacity to prevent duplicate mutexes

Lowering session_locks_capacity to 2,000 makes capacity eviction much more likely in long-lived/high-fanout sessions, and this cache stores live per-address mutexes used by Client::session_lock_for to serialize Signal session access. Once a key is evicted while an old Arc<Mutex<()>> is still held, the next lookup creates a second mutex for the same address, so concurrent encrypt/decrypt paths can run without mutual exclusion and race session state updates. The adjacent comment already documents that eviction here breaks serialization, so this default reduction introduces a real correctness risk rather than just a memory tradeoff.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/socket/noise_socket.rs`:
- Around line 47-49: The comment above the async_channel::bounded::<SendJob>(8)
creation contains a hardcoded latency claim ("<1ms each") which is incorrect
because transport.send(...).await introduces network-dependent latency; update
the comment for send_job_tx/send_job_rx to remove the timing assertion and
instead explain the rationale for capacity=8 in terms of memory usage and
backpressure (e.g., small fixed buffer to limit in-memory queued SendJob items
and to provide modest buffering for the serial sender), keeping the explanation
focused on "why" not timing guarantees.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3cc76424-51b3-41cf-afaf-72b9822105b6

📥 Commits

Reviewing files that changed from the base of the PR and between fd1ba33 and ed06f9c.

📒 Files selected for processing (2)
  • src/pdo.rs
  • src/socket/noise_socket.rs
💤 Files with no reviewable changes (1)
  • src/pdo.rs

Comment on lines +47 to +49
// Create channel for send jobs. Buffer of 8 is sufficient since the
// sender task processes jobs serially in <1ms each.
let (send_job_tx, send_job_rx) = async_channel::bounded::<SendJob>(8);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid hardcoded latency claims in the queue-capacity comment.

Line 48 says jobs complete in “<1ms each,” but this path includes transport.send(...).await, so latency is network-dependent and not guaranteed. Keep the comment rationale-focused (memory/backpressure) instead of fixed timing.

Suggested patch
-        // Create channel for send jobs. Buffer of 8 is sufficient since the
-        // sender task processes jobs serially in <1ms each.
+        // Keep this queue small to cap pre-allocation and apply backpressure
+        // during bursts; default tuned for typical single-client usage.
As per coding guidelines, "Keep code comments focused on the 'why', not the 'what'; avoid verbose explanations".
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Create channel for send jobs. Buffer of 8 is sufficient since the
// sender task processes jobs serially in <1ms each.
let (send_job_tx, send_job_rx) = async_channel::bounded::<SendJob>(8);
// Keep this queue small to cap pre-allocation and apply backpressure
// during bursts; default tuned for typical single-client usage.
let (send_job_tx, send_job_rx) = async_channel::bounded::<SendJob>(8);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/socket/noise_socket.rs` around lines 47 - 49, The comment above the
async_channel::bounded::<SendJob>(8) creation contains a hardcoded latency claim
("<1ms each") which is incorrect because transport.send(...).await introduces
network-dependent latency; update the comment for send_job_tx/send_job_rx to
remove the timing assertion and instead explain the rationale for capacity=8 in
terms of memory usage and backpressure (e.g., small fixed buffer to limit
in-memory queued SendJob items and to provide modest buffering for the serial
sender), keeping the explanation focused on "why" not timing guarantees.

Remove tokio-native from default features. Single-thread runtime
saves ~500KB peak memory and matches WA Web's single-thread model.
Handles ~3K msg/sec sustained which covers typical bot usage.

High-throughput deployments can opt in via features = ["tokio-native"]
to get multi-thread runtime (5K+ msg/sec).

Also enables cooperative yielding in TokioRuntime since single-thread
depends on it for fair task scheduling.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/runtime_impl.rs`:
- Around line 33-35: The current-thread runtime impl provides yield_now() but
still inherits the default yield_frequency() (10), which is too high for a
single-threaded scheduler; add an override for the yield_frequency() method in
the same impl (implement yield_frequency(&self) -> usize) and return a much
smaller value (e.g., 1) so the current-thread runtime yields more frequently and
avoids starving the event loop; update the impl that contains yield_now() to
include this yield_frequency() override.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 41d58659-2272-45ce-a2c1-a51092b9c54c

📥 Commits

Reviewing files that changed from the base of the PR and between ed06f9c and cf9875d.

📒 Files selected for processing (4)
  • Cargo.toml
  • examples/benchmark.rs
  • src/main.rs
  • src/runtime_impl.rs
💤 Files with no reviewable changes (1)
  • Cargo.toml

Comment thread src/runtime_impl.rs
TokioRuntime::yield_frequency now uses Handle::current().runtime_flavor()
to return 1 for current-thread (yield every frame, avoid starvation)
or 10 for multi-thread (yield infrequently, minimize overhead).

Works correctly for both runtime modes without user configuration.
@jlucaso1 jlucaso1 changed the title perf: reduce over-sized cache and channel pre-allocations perf: reduce pre-allocations, default to single-thread runtime Apr 16, 2026
@jlucaso1
jlucaso1 marked this pull request as draft July 1, 2026 12:23
@jlucaso1 jlucaso1 closed this Jul 20, 2026
@jlucaso1
jlucaso1 deleted the perf/reduce-over-allocations branch August 18, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant