|
| 1 | +# TICKET_484_1: FixedPrice Serialization + SocketBridge Buffer Optimization |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +Two hot-path optimizations applied to the E2E pipeline: |
| 6 | + |
| 7 | +1. **FixedPrice serialization**: Replaced `snprintf("%.8g")` with integer-only formatting |
| 8 | +2. **SocketBridge buffer**: Replaced per-message `memmove()` with read/write index tracking |
| 9 | + |
| 10 | +## Changes |
| 11 | + |
| 12 | +### 1. FixedPrice serialization (`trailer.hpp`) |
| 13 | + |
| 14 | +**Before**: `FixedPrice::raw` (int64_t) -> `to_double()` -> `snprintf("%.8g", d)` -> string_view |
| 15 | +**After**: `FixedPrice::raw` (int64_t) -> integer division/modulo -> direct digit output -> string_view |
| 16 | + |
| 17 | +- Eliminates float conversion and libc `snprintf` call on every price field |
| 18 | +- Each NOS->ER round-trip hits this path 2+ times (NOS price, ER avg_px) |
| 19 | +- Also removes `<cstdio>` include from header |
| 20 | + |
| 21 | +### 2. SocketBridge buffer (`socket_bridge.hpp`) |
| 22 | + |
| 23 | +**Before**: After each `parser_.feed()`, `memmove()` shifts unconsumed bytes to buffer start |
| 24 | +**After**: Track `read_pos_` / `write_pos_` indices; only compact when read_pos exceeds half the buffer |
| 25 | + |
| 26 | +- Eliminates memmove on every message in ping-pong mode |
| 27 | +- Under burst (N messages in buffer), avoids N-1 memmoves |
| 28 | + |
| 29 | +## Benchmark: Before vs After |
| 30 | + |
| 31 | +Environment: Linux x86_64, GCC, TCP loopback, 10000 iterations, 2000 warmup |
| 32 | + |
| 33 | +### Round-trip Latency (NOS -> ER, chrono) |
| 34 | + |
| 35 | +| Metric | Before | After | Change | |
| 36 | +|--------|--------|-------|--------| |
| 37 | +| P50 | 10.48 us | 10.31 us | -1.6% | |
| 38 | +| P90 | 18.21 us | 10.73 us | **-41.1%** | |
| 39 | +| P99 | 21.27 us | 12.13 us | **-43.0%** | |
| 40 | +| P99.9 | 1545.45 us | 14.42 us | **-99.1%** | |
| 41 | +| Max | 2971.99 us | 25.65 us | **-99.1%** | |
| 42 | +| Mean | 16.37 us | 10.41 us | **-36.4%** | |
| 43 | +| Stddev | 78.82 us | 0.47 us | **-99.4%** | |
| 44 | +| P99/P50 | 2.03x | 1.18x | **-41.9%** | |
| 45 | +| Throughput | 61,077 rt/s | 96,024 rt/s | **+57.2%** | |
| 46 | + |
| 47 | +### Round-trip Latency (RDTSC) |
| 48 | + |
| 49 | +| Metric | Before | After | Change | |
| 50 | +|--------|--------|-------|--------| |
| 51 | +| P50 | 16.67 us | 10.28 us | **-38.3%** | |
| 52 | +| P90 | 18.07 us | 10.53 us | **-41.7%** | |
| 53 | +| P99 | 21.49 us | 12.37 us | **-42.4%** | |
| 54 | +| P99.9 | 1963.00 us | 13.87 us | **-99.3%** | |
| 55 | +| Max | 3127.44 us | 36.42 us | **-98.8%** | |
| 56 | +| Throughput | 49,877 rt/s | 97,000 rt/s | **+94.5%** | |
| 57 | + |
| 58 | +### Half-trip (build + serialize + send) |
| 59 | + |
| 60 | +| Metric | Before | After | Change | |
| 61 | +|--------|--------|-------|--------| |
| 62 | +| P50 | 4.31 us | 4.17 us | -3.2% | |
| 63 | +| Min | 3.82 us | 3.76 us | -1.6% | |
| 64 | + |
| 65 | +### Sustained Throughput |
| 66 | + |
| 67 | +| Burst Size | Before (msg/s) | After (msg/s) | Change | |
| 68 | +|------------|----------------|---------------|--------| |
| 69 | +| 100 | 200,833 | 211,158 | +5.1% | |
| 70 | +| 1000 | 209,607 | 225,855 | +7.8% | |
| 71 | +| 5000 | 207,209 | 218,762 | +5.6% | |
| 72 | + |
| 73 | +## Analysis |
| 74 | + |
| 75 | +The dominant improvement is tail latency reduction. Before optimization, occasional `snprintf` slow paths and repeated `memmove` on batch receives caused P99.9 spikes into the millisecond range. After optimization: |
| 76 | + |
| 77 | +- P99.9 dropped from **1.5-2.0 ms to 13-14 us** (100x improvement) |
| 78 | +- Stddev dropped from **78-99 us to 0.47-0.53 us** (150x improvement) |
| 79 | +- Throughput improved **57-94%** due to eliminating tail latency outliers |
| 80 | + |
| 81 | +The P50 improvement (~3 us on RDTSC) is consistent with removing 2x snprintf calls per round-trip. |
| 82 | + |
| 83 | +## Notes |
| 84 | + |
| 85 | +- Benchmark on shared VM, not isolated cores. Absolute numbers will vary. |
| 86 | +- TCP loopback adds ~8-9 us baseline (kernel + network stack). |
| 87 | +- SocketBridge compaction threshold is BufferSize/2 (4KB for default 8KB buffer). |
0 commit comments