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}