code wiki / (root) / nx_rate_ema.nx

nx_rate_ema.nx source

↩ module page · 47 lines · 2242 B

1// nx_rate_ema.nx -- rolling per-peer download-rate accounting (bits-up, deterministic). 2// 3// module: nishi-core.torrent.rate_ema 4// depends: nx_syscalls.nx 5// capability: CORE_COMPUTE 6// wired_status: FULLY_WIRED 7// 8// The fuel the choker runs on: tit-for-tat needs a per-peer "bytes/sec we got from them" 9// number, and a real client must DERIVE it from raw byte counters, smoothed so one bursty 10// interval does not flip the unchoke set. Fixed-point integer EMA (no float, no wall-clock 11// in the math itself -- the caller passes the measured interval), so the smoothed rates -- 12// and therefore every choke decision built on them -- stay bit-for-bit replayable. Writes 13// straight into the choker's rates[] array (nx_ch_select_top_k consumes it unchanged). 14 15import "nx_syscalls.nx" 16 17const NX_RE_SCALE: i64 = 1000 // default fixed-point denominator for alpha 18const NX_RE_ALPHA_FAST: i64 = 500 // alpha=0.5: responsive (half weight on the new sample) 19const NX_RE_ALPHA_SLOW: i64 = 125 // alpha=0.125: smooth (classic TCP-RTT-style 1/8) 20 21// instantaneous rate: bytes seen over an interval -> bytes/sec. 0 if interval is non-positive. 22func nx_re_rate_sample(bytes_delta: i64, interval_ms: i64) -> i64 { 23 if interval_ms <= 0 { return 0 } 24 return (bytes_delta * 1000) / interval_ms 25} 26 27// fixed-point EMA step: ema' = (alpha*sample + (scale-alpha)*ema) / scale. 28// alpha is clamped to [0, scale]; scale<=0 is degenerate and returns the raw sample. 29func nx_re_ema_update(ema_old: i64, sample: i64, alpha: i64, scale: i64) -> i64 { 30 if scale <= 0 { return sample } 31 var a: i64 = alpha 32 if a < 0 { a = 0 } 33 if a > scale { a = scale } 34 return (a * sample + (scale - a) * ema_old) / scale 35} 36 37// update EVERY peer's smoothed rate from its byte-delta over the same interval. rates[] is the 38// choker's input array (in/out); bytes_delta[] is the per-peer bytes received since last tick. 39func nx_re_update_all(rates: *i64, bytes_delta: *i64, npeers: i64, interval_ms: i64, alpha: i64, scale: i64) -> i64 { 40 var i: i64 = 0 41 while i < npeers { 42 let s: i64 = nx_re_rate_sample(bytes_delta[i], interval_ms) 43 rates[i] = nx_re_ema_update(rates[i], s, alpha, scale) 44 i = i + 1 45 } 46 return 0 47}