G0: dich chinh - khong co drift bridge (run2 drained=True, recv=sent=10000), rut de xuat bu drift
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
|
||||
Ngày: 2026-08-27. Probe: `native_bridge/debug/g0_latency_probe.py` (chạy với
|
||||
`timeBeginPeriod(1)`, 10k block/config, pipeline thật Python→SHM→fx_vst_bridge→SHM).
|
||||
Log: `native_bridge/debug/g0_probe_run_full.log`.
|
||||
Log: `native_bridge/debug/g0_probe_run_full.log` (run 1),
|
||||
`native_bridge/debug/g0_probe_run2.log` (run 2 — có `pump_rate` + drain đầy đủ).
|
||||
|
||||
## 1. Bảng latency plugin (getLatencySamples, @48kHz)
|
||||
|
||||
@@ -24,20 +25,21 @@ trước thiếu nó → Sleep 15.6ms granularity → pump ~22ms/batch ≠ 21.33
|
||||
→ pureComp bị "stall 1.9s" giả. Với pacing chuẩn, stall đó biến mất — là
|
||||
artifact probe, KHÔNG phải VST stall.
|
||||
|
||||
| Config | mean ms | σ | p50 | p99 | max | recv/sent |
|
||||
|---|---|---|---|---|---|---|
|
||||
| empty | 21.54 | 1.74 | 21.64 | 22.03 | 25.73 | 9996/10000 |
|
||||
| pureComp | 21.69 | 0.26 | 21.66 | 22.02 | 28.94 | 9996/10000 |
|
||||
| Ozone 11 | 21.68 | 0.15 | 21.65 | 22.02 | 23.20 | 9996/10000 |
|
||||
| Config | mean ms | σ | p50 | p99 | max | recv/sent | pump_rate | drained |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| empty | 21.67 | 0.88 | 21.70 | 22.03 | 26.51 | 10000/10000 | 184.8 | ✅ |
|
||||
| pureComp | 21.69 | 0.15 | 21.67 | 22.03 | 24.07 | 10000/10000 | 184.9 | ✅ |
|
||||
| Ozone 11 | 21.70 | 0.14 | 21.68 | 22.02 | 22.20 | 10000/10000 | 184.8 | ✅ |
|
||||
|
||||
- Round-trip ≈ 21.5–21.7ms = đúng 1 batch 4 block (21.33ms) + overhead nhỏ
|
||||
(SHM copy + bridge proc 0.001–0.45ms). Latency plugin KHÔNG nằm trong
|
||||
round-trip — plugin latency bù bằng PDC (không chặn pipeline).
|
||||
- σ round-trip: 0.15–0.26ms (pureComp/Ozone) — RẤT nhỏ → **Gate G2 MỞ**: PDC
|
||||
- **`drained=True` mọi config (run 2): bridge xử lí HẾT 10000 block, recv = sent
|
||||
— KHÔNG mất block, KHÔNG backlog tích lũy.** Đây là bằng chứng quyết định:
|
||||
không có drift thật (xem §4).
|
||||
- σ round-trip: 0.14–0.15ms (pureComp/Ozone) — RẤT nhỏ → **Gate G2 MỞ**: PDC
|
||||
mẫu có ý nghĩa, không cần giảm jitter trước (không cần relay WS→SHM sớm).
|
||||
- empty σ=1.74: 1 outlier khởi động (min=0.04ms), p50/p99 vẫn 21.6/22.0.
|
||||
- recv=9996 < 10000: 4 block cuối chưa drain hết lúc probe kết thúc (bridge
|
||||
chậm hơn pace probe — xem §4), không phải mất block.
|
||||
- empty σ=0.88: 1 outlier khởi động (min=0.05ms), p50/p99 vẫn 21.7/22.0.
|
||||
|
||||
## 3. Inter-arrival deviation
|
||||
|
||||
@@ -55,20 +57,27 @@ Block về theo batch 4 (bridge xử lí take=4/lần) → inter-arrival lệch
|
||||
|
||||
| Nguồn | procAvg | procMax | loopAvg | loopMax | rate blk/s | vs realtime 187.5 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| probe empty | 0.001ms | 0.05ms | 22.0ms | 1010ms | 181.7 | −3.1% |
|
||||
| probe pureComp | 0.45ms | 0.80ms | 22.0ms | 1012ms | 181.6 | −3.1% |
|
||||
| probe Ozone | 0.14ms | 0.35ms | 22.0ms | 1008ms | 181.7 | −3.1% |
|
||||
| probe empty | 0.001ms | 0.06ms | 22.0ms | 1017ms | 181.5 | −3.2% |
|
||||
| probe pureComp | 0.46ms | 1.37ms | 22.0ms | 1011ms | 181.6 | −3.1% |
|
||||
| probe Ozone | 0.14ms | 0.71ms | 22.0ms | 1015ms | 181.5 | −3.2% |
|
||||
| app thật (master chain đầy) | 3.4ms | 10.0ms | 21.4ms | 156ms | 186.5 | −0.5% |
|
||||
|
||||
- **Rate "chậm 3% (probe) / 0.5% (app)" KHÔNG phải drift thật — artifact đo.**
|
||||
Bằng chứng: run 2 `drained=True`, recv=sent=10000 mọi config — nếu bridge
|
||||
thật sự chậm 3%, backlog 300+ block phải tích lại và drain phải mất >1.5s
|
||||
sau pump (thực tế drain xong ngay trong vòng lặp pump, `pump_rate` chỉ thấp
|
||||
vì probe tự sleep lệch pha với nhịp bridge poll 1ms + granularity sleep).
|
||||
Bridge xử lí ASAP khi có input (`avail>0` → process ngay) — không có pace
|
||||
riêng để "chậm"; output rate = input rate. App thật rate 186.5 thấp hơn vì
|
||||
runSec gồm idle trước khi audio chảy (chain load, chờ play).
|
||||
- **loopMax ≈ 1s ở MỌI config probe kể cả empty** — spike 1s/lần chạy ~53s,
|
||||
không phải VST (empty cũng có). Không xuất hiện trong log app thật gần nhất
|
||||
(loopMax 156ms) — nghi Windows scheduler/antivirus/power, cần theo dõi khi
|
||||
chạy thật. Probe round-trip max vẫn ≤29ms → spike xảy ra lúc bridge idle,
|
||||
không chặn block đang xử lí.
|
||||
- **Drift: bridge chậm 0.5–3.1% so realtime** (rate < 187.5). App thật −0.5%
|
||||
(186.5): mỗi giây thiếu ~1 block → FILL 8 block (42.7ms) cạn sau ~8s nếu
|
||||
không có cơ chế bù. Probe −3.1% là đo có pace probe riêng (nhịp probe + nhịp
|
||||
bridge lệch nhau); con số tin cậy cho drift thật là từ app: −0.5%.
|
||||
chạy thật. Probe round-trip max vẫn ≤27ms → spike xảy ra lúc bridge idle,
|
||||
không chặn block đang xử lí (không gây underrun).
|
||||
- **Kết luận: KHÔNG cần bù drift.** FILL 8 block + underrun-fade/resume hiện
|
||||
có đủ — u=3/session (fx_stats) hiếm, do main-thread stall (GUI), không phải
|
||||
drift đều. Đề xuất resync/bù rate ban đầu rút lại (xem §6).
|
||||
|
||||
## 5. ~0.22s gồm gì (xác nhận)
|
||||
|
||||
@@ -93,12 +102,12 @@ Latency path thực đo + hằng số app:
|
||||
|
||||
## 6. Kết luận cho G2+
|
||||
|
||||
1. **Gate G2: MỞ.** σ round-trip 0.15–0.26ms → PDC mẫu có nghĩa; không cần
|
||||
1. **Gate G2: MỞ.** σ round-trip 0.14–0.15ms → PDC mẫu có nghĩa; không cần
|
||||
relay WS→SHM trước G2.
|
||||
2. **Drift bridge −0.5% là phát hiện mới** (không có trong plan §1–§8): FILL
|
||||
cạn dần trên session dài → cần quyết định: (a) chấp nhận (underrun hiếm, có
|
||||
resume), (b) bù/resync định kỳ, (c) thêm giám sát rate. **Báo user để review
|
||||
trước khi đưa vào plan.**
|
||||
2. **Không có drift bridge** (đã kiểm chứng run 2: drained=True, recv=sent).
|
||||
Không cần bù rate/resync — FILL 8 + underrun-fade/resume đủ. Tránh thêm
|
||||
code không cần thiết. Nếu sau này thấy underrun đều trên session dài →
|
||||
đo lại `pump_rate`/`drained` trước khi kết luận drift.
|
||||
3. **loopMax ~1s spike** (probe, cả empty): theo dõi, chưa thấy ở app thật.
|
||||
4. Plugin latency bảng §1 đủ cho G2: `graph.getLatencySamples()` sẽ thay hằng
|
||||
số; dữ liệu hiện tại xác nhận pureComp là plugin duy nhất có latency thật.
|
||||
|
||||
@@ -69,6 +69,7 @@ def run(name, chain, n_blocks):
|
||||
next_idx = 0
|
||||
batch_dur = PERIOD * 4
|
||||
batches = n_blocks // 4
|
||||
pump_t0 = time.perf_counter()
|
||||
for b in range(batches):
|
||||
t0 = time.perf_counter()
|
||||
for _ in range(4):
|
||||
@@ -83,6 +84,19 @@ def run(name, chain, n_blocks):
|
||||
dt = time.perf_counter() - t0
|
||||
if dt < batch_dur:
|
||||
time.sleep(batch_dur - dt)
|
||||
# Drain phần còn lại (bridge xử lí nốt backlog) — nếu bridge theo kịp
|
||||
# input, recv đủ = sent ngay sau pump; thiếu kéo dài = drift thật.
|
||||
drain_t0 = time.perf_counter()
|
||||
while len(recv_t) < len(send_t) and time.perf_counter() - drain_t0 < 2.0:
|
||||
outs = sess.read_output()
|
||||
base = len(recv_t)
|
||||
for k in range(len(outs)):
|
||||
recv_t.append((base + k, time.perf_counter()))
|
||||
if not outs:
|
||||
time.sleep(0.002)
|
||||
pump_t1 = time.perf_counter()
|
||||
stats["pump_rate"] = len(recv_t) / (pump_t1 - pump_t0)
|
||||
stats["drained"] = len(recv_t) == len(send_t)
|
||||
# round-trip per block
|
||||
rt = []
|
||||
for j, tr in recv_t:
|
||||
@@ -135,6 +149,8 @@ def main():
|
||||
st = run(name, chain, args.blocks)
|
||||
results[name] = st
|
||||
print("plugin_latency:", st.get("plugin_latency"))
|
||||
print("pump_rate=%.3f blk/s drained=%s (bridge theo kip input: rate ~187.5 va drained=True)" %
|
||||
(st.get("pump_rate", 0), st.get("drained")))
|
||||
rt = st.get("roundtrip_ms", {})
|
||||
print("roundtrip_ms n=%d mean=%.3f sd=%.3f min=%.3f p50=%.3f p99=%.3f max=%.3f" %
|
||||
(rt.get("n", 0), rt.get("mean", 0), rt.get("sd", 0), rt.get("min", 0),
|
||||
|
||||
Reference in New Issue
Block a user