v0.5.0: bounded-latency L3 orderbook, egui/wgpu refresh, cross-platform CI#1
Merged
Conversation
…-platform CI
Reworks the hot path of the flat ring-buffer L3 estimator so per-update
latency is bounded rather than growing with queue age, refreshes the
whole dependency tree onto the egui 0.35 / wgpu 29 generation, and makes
the three-platform release build actually reproducible in CI.
Bounded-latency L3 orderbook
----------------------------
The 65,536-tick (1 << 16) ring-buffer storage, the three-level
hierarchical bitsets, the pooled OrderBlock arena, and lazy window
re-centring are unchanged. The steady-state maintenance paths that
scaled with book size or queue age have been rewritten.
Top-N ranking. `is_in_top_n` no longer walks the bitset level by level
from the best price (O(N) per call, hit on every applied level change).
It now maps the price to its window index and answers the query with a
bounded hardware POPCNT over the occupancy bitmap:
- count_below_limited / count_above_limited sum set bits on the far
side of the index, stopping once the limit is reached, so the scan
is capped at N regardless of how many levels are occupied.
- A runtime `popcnt`-detected specialisation is dispatched once per
book (the detection flag is x86_64-only and cfg-gated out of the
struct entirely on other targets).
- Out-of-window prices are decided in O(1) from the total occupancy.
Cancellation. `remove_order_by_size` previously scanned the whole queue
from the oldest block forward, so exact-size cancel latency grew with
queue length and with the zeroed "holes" left by in-place removals.
The search now runs newest-to-oldest and is capped at the most recent
blocks (MAX_EXACT_MATCH_BLOCKS), so cancel cost is flat regardless of
how long a level has lived; a miss falls back to the existing pop_back
trim, so the aggregate level quantity is always exact. The AVX2 path is
now selected through a safe `remove_order_by_size` wrapper with an
always-available scalar fallback, replacing the `unsafe` public
`remove_order_by_size_simd`. Both paths guard head_offset, tail_offset
and non-zero sizes, so stale/unfilled slots can never false-match.
Snapshot / gap reset. `reset_with_base` no longer clears all 65,536
queue slots on every snapshot and gap recovery. It scans the merged
bid|ask occupancy words and frees only the queues that are actually
populated, relying on the maintained invariant "queue has blocks <=>
occupancy bit is set". `Bitset::set` is now idempotent (early-out when
the bit is already set) so it never re-touches the L1/L0 summary words.
Correctness fixes
-----------------
- Liquidity caches. `get_liquidity_curves` and `calculate_liquidity_
impact` shared one `last_liquidity_id`, so calling either marked the
other's cache fresh and could return stale curves/impact. Split into
independent `last_liquidity_curve_id` and `last_impact_book_id`.
- Empty snapshots are now authoritative: a snapshot with no levels
resets the book instead of being skipped, clearing stale levels.
- Snapshot fetch failures (`client.rs`) now check `error_for_status`,
abort the socket, surface a UI warning and retry, instead of logging
and continuing without a book.
- Depth-update buffering (`app.rs`) is capped (dropping the oldest when
full) and a sequence gap during snapshot replay triggers a refetch
rather than being silently ignored.
- TWAP binning aligns to absolute time (`ts - ts % bin_ms`), assigns the
boundary trade to the new bin, fills quiet gaps with empty bins
(bounded by the window), and bumps an `update_id` so the PSD-bar UI
cache invalidates on value changes, not just length changes.
Benchmark harness
-----------------
The order-book benches replayed updates with static sequence ids, so
after the first apply every update was rejected as stale and the loop
measured the no-op reject path. They now assign monotonic ids and assert
`DepthUpdateStatus::Applied`, so the numbers reflect real work.
Dependency refresh
------------------
Whole tree bumped to latest, with compatibility fixes:
- eframe / egui 0.32 -> 0.35, egui_plot 0.33 -> 0.36.
`App::update(&Context)` became `App::ui(&mut egui::Ui)` and panels'
`.show()` now take a `Ui`; the app clones the Arc-backed context from
`ui.ctx()`, drives the `CentralPanel` on the passed `Ui`, and keeps
the still-`Context`-based floating `Window`s. `NativeOptions` lost
`hardware_acceleration` (a glow-era GL hint, irrelevant to wgpu).
- wgpu 25 -> 29, pinned to 29 (not the latest 30) so the direct
Vulkan-backend dependency unifies with eframe 0.35's own wgpu
instead of pulling a second, unused copy.
- rand 0.9 -> 0.10: `random_range` moved to the `RngExt` trait
(engine + benches).
- criterion 0.5 -> 0.8: `criterion::black_box` -> `std::hint::black_box`.
- reqwest 0.12 -> 0.13, tokio-tungstenite 0.27 -> 0.30, tokio 1.46 ->
1.52, plus transitive patch bumps.
Cross-platform build / CI
-------------------------
- The wgpu backend that eframe enables (`metal` + `webgpu`) has no usable
native backend on Linux/Windows, which aborted at startup. A direct
`wgpu` dependency re-adds `vulkan` (+ vulkan-portability), unified into
eframe's wgpu; macOS keeps `metal`.
- `popcnt_available` was written but only read under
`cfg(target_arch = "x86_64")`, so it tripped `dead_code` under
`-D warnings` on the aarch64 macOS runner. The field and its
initialiser are now cfg-gated to x86_64.
- `Cargo.lock` is committed (removed from `.gitignore`) so the workflow's
`--locked` steps have a lockfile; without it all three jobs failed at
the first cargo command.
- Release workflow builds/tests/clippies on ubuntu, windows and macos
for every PR and push, and publishes the three binaries only on `v*`
tags.
Performance
-----------
On the corrected criterion benchmark, a fully applied depth update
(top-N ranking + L3 queue maintenance) drops from ~7.5 us to ~500 ns
(~15x), landing within ~1.4x of a plain L2 BTreeMap that does none of
the L3 work. More importantly, worst-case per-update latency is now
bounded instead of scaling with queue age.
Bumps the crate version to 0.5.0 and documents the storage/ranking
design and the throughput numbers under a new README "Performance"
section.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release candidate for v0.5.0. Opening to exercise the 3-platform (ubuntu/windows/macos) build matrix — build + test + clippy
-D warningson each OS. No release is published (that needs av*tag).See the commit message for full detail. Highlights:
popcnt_availablegating), wgpu Vulkan backend, committedCargo.lockfor--locked.