Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add token into stream #19

Closed
wants to merge 8 commits into from
Closed

Conversation

cs8425
Copy link

@cs8425 cs8425 commented Apr 24, 2017

#18
解決一個慢的stream餓死其他stream的問題
保留原本的token來控制總buffer的使用量
除非夠慢又夠多的stream才會造成餓死的問題
(stream的MaxBuffer * N >= session的MaxBuffer)

config的預設值目前我是抓比較高, 可以看情況調整
每個Session最多16MB (MaxReceiveBuffer)
每個Stream最多16kB (MaxStreamBuffer)
Stream buffer小於4kB的時候允許對面寫入新資料 (MinStreamBuffer)
預設值能允許1024個慢速stream才會造成餓死
假設不被惡意攻擊(對面無視cmdFUL控制封包繼續傳)
此情況下記憶體使用量 ~= 16MB

@xtaci
Copy link
Owner

xtaci commented Apr 25, 2017

你这样做最大的传输速度被限制在 16KB/RTT了

@cs8425
Copy link
Author

cs8425 commented Apr 25, 2017

實測過並不會限制在16KB/RTT
設成16KB, 4MB, ping 12ms, 一樣跑到94Mbps(我這邊的頻寬上限100Mbps)
設成4KB, 4MB, 下降為70Mbps
只有stream read的速度<對面傳的速度才會受制於stream的buffer

@xtaci
Copy link
Owner

xtaci commented Apr 25, 2017

你这个ping值太小,如果ping值一大,比如200ms,你速度就下来了

@cs8425
Copy link
Author

cs8425 commented Apr 25, 2017

我去租一台ping 200ms的主機試試@@
晚點再回報
不知道有沒有更好的模擬方法?

@cs8425
Copy link
Author

cs8425 commented Apr 25, 2017

scaleway的巴黎主機(VC1S)實測
ping 300~350ms的情況下差異並沒有很大
kcptun為master直接clone下來編譯的
只有smux有差異
是否有別的參數/測試建議?
ps.如果有需要主機可以直接開帳號給你測試

iperf server端皆為: iperf -s -p 9999 -f M -N
socks5 server已確認過不是瓶頸

改過的:

$ iperf -c 127.0.0.1 -p 5002 -f M -N -w 100M -t 60 -i 5
------------------------------------------------------------
Client connecting to 127.0.0.1, TCP port 5002
TCP window size: 0.41 MByte (WARNING: requested  100 MByte)
------------------------------------------------------------
[  3] local 127.0.0.1 port 51926 connected with 127.0.0.1 port 5002
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0- 5.0 sec  46.9 MBytes  9.38 MBytes/sec
[  3]  5.0-10.0 sec  40.1 MBytes  8.03 MBytes/sec
[  3] 10.0-15.0 sec  39.5 MBytes  7.90 MBytes/sec
[  3] 15.0-20.0 sec  40.1 MBytes  8.03 MBytes/sec
[  3] 20.0-25.0 sec  40.1 MBytes  8.03 MBytes/sec
[  3] 25.0-30.0 sec  40.8 MBytes  8.15 MBytes/sec
[  3] 30.0-35.0 sec  37.4 MBytes  7.47 MBytes/sec
[  3] 35.0-40.0 sec  42.1 MBytes  8.43 MBytes/sec
[  3] 40.0-45.0 sec  41.6 MBytes  8.32 MBytes/sec
[  3] 45.0-50.0 sec  41.0 MBytes  8.20 MBytes/sec
[  3] 50.0-55.0 sec  40.6 MBytes  8.12 MBytes/sec
[  3] 55.0-60.0 sec  40.9 MBytes  8.18 MBytes/sec
[  3]  0.0-60.1 sec   491 MBytes  8.18 MBytes/sec

http://beta.speedtest.net

原本的:

$ iperf -c 127.0.0.1 -p 5002 -f M -N -w 100M -t 60 -i 5
------------------------------------------------------------
Client connecting to 127.0.0.1, TCP port 5002
TCP window size: 0.41 MByte (WARNING: requested  100 MByte)
------------------------------------------------------------
[  3] local 127.0.0.1 port 51940 connected with 127.0.0.1 port 5002
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0- 5.0 sec  45.8 MBytes  9.15 MBytes/sec
[  3]  5.0-10.0 sec  40.5 MBytes  8.10 MBytes/sec
[  3] 10.0-15.0 sec  40.5 MBytes  8.10 MBytes/sec
[  3] 15.0-20.0 sec  40.6 MBytes  8.12 MBytes/sec
[  3] 20.0-25.0 sec  40.1 MBytes  8.03 MBytes/sec
[  3] 25.0-30.0 sec  40.4 MBytes  8.07 MBytes/sec
[  3] 30.0-35.0 sec  40.9 MBytes  8.18 MBytes/sec
[  3] 35.0-40.0 sec  40.1 MBytes  8.03 MBytes/sec
[  3] 40.0-45.0 sec  40.2 MBytes  8.05 MBytes/sec
[  3] 45.0-50.0 sec  40.9 MBytes  8.18 MBytes/sec
[  3] 50.0-55.0 sec  41.0 MBytes  8.20 MBytes/sec
[  3] 55.0-60.0 sec  40.6 MBytes  8.12 MBytes/sec
[  3]  0.0-60.0 sec   492 MBytes  8.19 MBytes/sec

http://beta.speedtest.net

client設定

2017/04/25 17:45:30 main.go:283: version: SELFBUILD
2017/04/25 17:45:30 main.go:319: listening on: 127.0.0.1:5002
2017/04/25 17:45:30 main.go:320: encryption: salsa20
2017/04/25 17:45:30 main.go:321: nodelay parameters: 0 30 2 1
2017/04/25 17:45:30 main.go:322: remote address: 212.47.246.125:5001
2017/04/25 17:45:30 main.go:323: sndwnd: 128 rcvwnd: 512
2017/04/25 17:45:30 main.go:324: compression: true
2017/04/25 17:45:30 main.go:325: mtu: 1350
2017/04/25 17:45:30 main.go:326: datashard: 1 parityshard: 0
2017/04/25 17:45:30 main.go:327: acknodelay: true
2017/04/25 17:45:30 main.go:328: dscp: 0
2017/04/25 17:45:30 main.go:329: sockbuf: 16777216
2017/04/25 17:45:30 main.go:330: keepalive: 10
2017/04/25 17:45:30 main.go:331: conn: 1
2017/04/25 17:45:30 main.go:332: autoexpire: 0
2017/04/25 17:45:30 main.go:333: scavengettl: 0
2017/04/25 17:45:30 main.go:334: snmplog: 
2017/04/25 17:45:30 main.go:335: snmpperiod: 60

server設定:

2017/04/25 09:45:29 main.go:297: version: SELFBUILD
2017/04/25 09:45:29 main.go:330: listening on: [::]:5001
2017/04/25 09:45:29 main.go:331: target: 127.0.0.1:9999
2017/04/25 09:45:29 main.go:332: encryption: salsa20
2017/04/25 09:45:29 main.go:333: nodelay parameters: 0 50 0 0
2017/04/25 09:45:29 main.go:334: sndwnd: 1024 rcvwnd: 1024
2017/04/25 09:45:29 main.go:335: compression: true
2017/04/25 09:45:29 main.go:336: mtu: 1350
2017/04/25 09:45:29 main.go:337: datashard: 1 parityshard: 0
2017/04/25 09:45:29 main.go:338: acknodelay: true
2017/04/25 09:45:29 main.go:339: dscp: 0
2017/04/25 09:45:29 main.go:340: sockbuf: 16777216
2017/04/25 09:45:29 main.go:341: keepalive: 10
2017/04/25 09:45:29 main.go:342: snmplog: 
2017/04/25 09:45:29 main.go:343: snmpperiod: 60
2017/04/25 09:45:29 main.go:344: pprof: false

@xtaci
Copy link
Owner

xtaci commented Apr 25, 2017

看了下,session.token你没有去掉, 总阀门没有关啊。 只是在当前的基础上,还增加了限速。

@xtaci
Copy link
Owner

xtaci commented Apr 25, 2017

我明白你的意思了,你是想通过接收方发送控制信息,告诉发送方停止推流。

发送方必须要执行cmdFUL,cmdEMP进行阻塞。

这种方法的主要的问题就是可能造成限速, 最终 cmdFUL, cmdEMP会成为驱动下一次数据发送的时钟信号,这个时钟信号的间隔就是RTT。

你上面的测试是一个快速的consumer,可能并没有触发到cmdFUL/EMP。也就和原来一样的结果。

@cs8425
Copy link
Author

cs8425 commented Apr 25, 2017

session.token沒去掉是為了限制記憶體的總使用量
限速是針對個別的stream
讀取慢的stream可以通知對面對應的stream不要再傳資料
畢竟我們的最上層是TCP
如果不通知的話
有這種可能:
A端的ID 1拼命傳資料(ex: 上傳測試)
B端對應的stream無法達到那麼快的速度
於是A端的ID 1會傳到整個session的token用完(TCP還能寫入就會不斷丟)
造成其他stream一起卡住...

那我這個設計只是加入一個通知的功能
當B端對應的stream無法達到那麼快的速度
會卡住A端的寫入
這麼一來session的token就不會被用完
也就不會造成其他stream一起卡住的問題

是的
理論上是能解決原本issue
至於這個"限速"的後遺症倒是還好
畢竟就是因為太慢才會觸發這個機制

ok
先吃個飯
晚點測試其他的方法
一邊跑youtube一邊跑測速行嗎?
有沒有好一點的測試方法可以建議呢?

@xtaci
Copy link
Owner

xtaci commented Apr 25, 2017

问题的核心在于,在什么条件下去通知发送方,阈值什么是合理的,16KB是拍脑袋?

为什么不是在 stream buffer 达到整体半窗(MaxReceiveBuffer/2)的时候.

@cs8425
Copy link
Author

cs8425 commented Apr 25, 2017

好的
那我先解釋一下MaxStreamBufferMinStreamBuffer的關係
一個有FIFO buffer的高速系統
一般設計上會用總大小的20%當(快要)空 80%當(快要)滿的時候傳送訊號
這樣不會浪費buffer也不會造成overflow
(這類的設計在MCU之類的硬體系統很常見, 有些會選30:70、25:75、10:90之類的)
16KB => 80%
4KB => 20%
當然更進階一點的就是設計一個簡單的估測系統(跟RTT、讀寫速度有關)
預估什麼時候送cmdFUL不會造成overflow也不會剩下太多空間
什麼時候送cmdEMP可以在buffer真的被讀到空之前收到新資料

至於MaxStreamBuffer為何選16KB
我是隨手抓沒錯啦
不過還是有點道理的
假設一個(外部無瓶頸)高寫高讀的情況:

  1. 讀取端的速度會被侷限於read的頻率&一次讀取的量
  2. Buffer至少要保留write完到下次read的資料量才不會影響寫入速度

然而read/write的頻率是有上限的
我沒搞錯的話
在linux kernel跟Internal kernel timer frequency有關
是1000Hz (編譯kernel的時候能調整), 超過就要丟到kernel的buffer/queue
16KB * 1000Hz = 128Mbps ~= 16MB/s
我想應該沒有多少人的頻寬可以支撐N條速度超過128Mbps的stream吧...?
希望我這樣解釋夠清楚@@

@xtaci
Copy link
Owner

xtaci commented Apr 26, 2017

  1. 也就是说16KB是经验值, 但是我说的一半也是有道理的, 在cmdFUL到达发送方的半个RTT时间内,剩下的来自发送方的数据流在这个时间刚好把buffer填满。

  2. 这样貌似解决的问题是慢速receiver导致卡住的问题,但没有解决快速sender+receiver抢占带宽的问题。 也就是流之间的QoS balance。这个问题存在根本的复杂性。

@cs8425
Copy link
Author

cs8425 commented Apr 26, 2017

為何我不用選MaxReceiveBuffer / 2
因為用這個數值的話
通知對面暫停的時機太晚
有可能
1.速度不夠, 根本用不到那麼大的buffer, 從頭到尾幾乎都沒觸發
2.只要少量的stream觸發這個機制, 很容易造成整個session卡住

正常來說stream buffer使用量會是一個夠小但是變化頻率高的數值
(buffer只保留write完到下次read的資料量, 大大你可以trace一下session.token跟stream.token的數值變化)
太大的stream buffer(相對於MaxReceiveBuffer)效果反而不好

是解決少數慢速receiver造成卡住的問題沒錯
如果要stream之間的QoS問題
可以在這個基礎上加個map計算&紀錄每個stream的速度
大概這幾個速度要紀錄: 下載方向的讀寫、上傳方向的寫、上傳方向對面的讀(這個比較不準, 靠cmdFULcmdEMP回授而已, 可能需要在格式加入精確數值的欄位)
速度超標的就通知對面暫停寫入
自己這邊也可以暫停自己的寫入
詳細的頻寬分配方法/條件就要再思考思考了
可以考慮丟個handler function給上層應用來搞XD

另外我發現我命名好像不是那麼好
stream.markFUL()換成stream.pauseWrite()
stream.markEMP()換成stream.resumeWrite()
這樣應該會比較好理解

@xtaci
Copy link
Owner

xtaci commented Apr 26, 2017

修改名字是可以的,另外,这个改动不兼容原来的代码,会造成session.go:273的退出,需要一个开关。

另外 CI 目前还过不了,你需要测试下:

  - go test -coverprofile=coverage.txt -covermode=atomic -bench .

@cs8425
Copy link
Author

cs8425 commented Apr 26, 2017

在config裡面加入一個bool來開關行嗎?
預設要關閉還是開啟?
順便問一下, protocol version通常是什麼時候才會增加/改變?

test的部份我覺得蠻奇怪的
在我電腦上測試是會過的
CI不知道為啥沒過

$ go test -coverprofile=coverage.txt -covermode=atomic -bench .
test -coverprofile=coverage.txt -covermode=atomic -bench .
BenchmarkAcceptClose-2   	   50000	     58236 ns/op
BenchmarkConnSmux-2      	     100	  11094876 ns/op	  11.81 MB/s
BenchmarkConnTCP-2       	   10000	    109450 ns/op	1197.54 MB/s
PASS
coverage: 90.5% of statements
ok  	_/home/cs8425/code/smux	11.486s

@xtaci
Copy link
Owner

xtaci commented Apr 26, 2017

预设应该同之前的版本保持一致,用法无需改动。

@codecov-io
Copy link

codecov-io commented Apr 26, 2017

Codecov Report

Merging #19 into master will increase coverage by 1.04%.
The diff coverage is 94.89%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master     #19      +/-   ##
=========================================
+ Coverage   89.06%   90.1%   +1.04%     
=========================================
  Files           4       4              
  Lines         384     475      +91     
=========================================
+ Hits          342     428      +86     
- Misses         37      40       +3     
- Partials        5       7       +2
Impacted Files Coverage Δ
frame.go 100% <ø> (ø) ⬆️
session.go 89.49% <100%> (+1.23%) ⬆️
mux.go 94.28% <77.77%> (-5.72%) ⬇️
stream.go 89.42% <95.38%> (+2.52%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2de5471...a72c8d6. Read the comment docs.

@xtaci
Copy link
Owner

xtaci commented Apr 26, 2017

带stream.token
2017-04-26 2 02 26

原始
2017-04-26 2 04 03

实测对于youtube播放,有明显的速度下降,应该就是stream流控所致。

youtube的视频流会应该会通过阻塞读取(暂停读取)来控制TCP推流速度。但加上stream.token的流控,确实又加了一层 cmdFUL/EMP的时钟,或者说类似于TCP_CORK的效果。

@cs8425
Copy link
Author

cs8425 commented Apr 26, 2017

算是trade off吧
剛連線的時候的確無法迅速提高throughput
有個改法是剛連線的stream buffer給比較大
過了幾秒再調回正常(要幾秒可能要思考一下)
代價是一次開多個連線的時候記憶體使用量會瞬間飆高
等速度穩定之後才會降回去
雖然個人認為youtube播放只要跟的上不卡就行...
大大你覺得呢?
剛開的連線給先給比較大的stream buffer這個設計是否有必要?

@xtaci
Copy link
Owner

xtaci commented Apr 26, 2017

我还是觉得,这个问题有根本的困难,内存总用量和流量平衡不可兼得。
可以继续探讨。

@cs8425
Copy link
Author

cs8425 commented Apr 26, 2017

列一下幾個已知條件/現象:

  • 所有stream buffer用量加起來不能超過MaxReceiveBuffer, 否則全部會卡住
  • cmdFUL/EMP從發送到實際作用會受RTT影響
  • 初始throughput的爬升會受stream buffer大小影響
  • throughput穩定之後, stream buffer使用量應該接近0

那我試試看改個這種東西出來:
1.剛連線的stream buffer設為MaxReceiveBuffer/2 (或是除其他數, 儘量大但是不要卡住session)
2.觀察stream buffer使用量, 當前用量開始變小(只在0跟上次最大用量之間跳), 判定為進入穩定
3.進入穩定之後stream buffer重設成某個較小的數值(ex: 16KB)
理論上應該有用....晚點試試...
RTT部份還在想要怎估測發送時機比較好

@cs8425 cs8425 force-pushed the move-token-to-stream branch from 3acabae to 41aab74 Compare April 27, 2017 19:11
@cs8425
Copy link
Author

cs8425 commented Apr 29, 2017

@xtaci
大大你再試試這個版本
加入了RTT測試(收到cmdNOP回傳cmdACK)
簡單估算cmdFULcmdEMP發送時機跟stream buffer應該給多少
用自己嚕的TCP over TCP跑youtube測試,
c6b175f 比, 初始爬升快了不少, 最終速度高了大約25%
但是其他iperf之類的速度測試結果差不多就是了

@cs8425 cs8425 force-pushed the move-token-to-stream branch from 431da70 to abaf4a6 Compare April 29, 2017 21:28
@cs8425 cs8425 force-pushed the move-token-to-stream branch from abaf4a6 to 31ccec0 Compare April 29, 2017 22:06
@cs8425
Copy link
Author

cs8425 commented May 1, 2017

重編了kcptun
其他測試參數跟上次一樣
不知道為啥今天比較快@@

重點在throughput變化的部份
原本的smux初期會衝到40Mbps左右, 然後再降回25Mbps左右
c6b175f 是一路慢慢爬到20Mbps左右測試就結束了
31ccec0 衝到25Mbps左右就維持在那個位置
上傳部份可能頻寬不夠大看不太出來

目前版本 31ccec0:
kcptun-smux-31ccec0

c6b175f:
kcptun-smux-c6b175f

原本的:
kcptun-smux-org

@xtaci
Copy link
Owner

xtaci commented May 2, 2017

降速是无论如何也不能接受的,影响的用户量太大了。

@cs8425
Copy link
Author

cs8425 commented May 2, 2017

31ccec0 並沒有降速啊
是直接爬到頻寬上限就穩定了
而原本的會超過頻寬上限之後才回歸正常
超過的部份就是靠記憶體再撐
所以 31ccec0 如果要達到一樣的效果
把buffer限制開大一點即可

@xtaci
Copy link
Owner

xtaci commented May 2, 2017

我先观察一下

@xtaci
Copy link
Owner

xtaci commented May 3, 2017

未来两周会有点忙,后面再回复,暂时没有时间验证这个,告知一下。

@cs8425
Copy link
Author

cs8425 commented May 3, 2017

要不要另外編譯一版kcptun
開個issue特別著名一下是測試版
交給大家決定要不要當白老鼠&回報結果?
如果不行也沒關係
就等大大有空摟

@xtaci
Copy link
Owner

xtaci commented May 3, 2017

不急,慢慢来,先思考一下。

@xtaci
Copy link
Owner

xtaci commented May 25, 2017

抱歉晚回复,前几周工作上的事有点忙。

@xtaci
Copy link
Owner

xtaci commented May 26, 2017

我始终觉得:

  1. 最大吞吐量
  2. stream之间流量平衡(stream饿死)
  3. 内存总量一定

这三者最多同时满足两者。

@cs8425
Copy link
Author

cs8425 commented May 26, 2017

大致上正確
第3點不是總量一定
應該說 RAM要小於 "最大吞吐量所需的RAM" x "stream數量"
而且3者之間的分配vs效果
愈接近極限 會愈沒有效果
假設最大吞吐量要 32kB / stream
95%的最大吞吐量可能只要 16kB / stream
如果RAM的使用量真的無法妥協
我會偏好降個幾%的最大吞吐量
來換(幾乎)所有stream不會卡住
畢竟...youtube點下去播了幾秒發現這不是想要的
點其他影片連結就整個卡住其實還蠻惱人的@@

@jannson
Copy link
Contributor

jannson commented Mar 6, 2019

@cs8425 有空更新一个最新版本么?感觉你说得有道理,我想测试测试。

@cs8425
Copy link
Author

cs8425 commented Mar 6, 2019

@jannson 我直接merge了xtaci的v1.1.1到我的master 7f8c639
你可以先試試看效果
改的有點亂
要提交PR回這邊的話還要多花點功夫...

@jannson
Copy link
Contributor

jannson commented Mar 6, 2019

非常感谢,一回家就开始测试了,手动 merge 了一波。直观感觉效果挺好的~~~ 具体参数还没提供。
看了代码,可惜就是无法无缝兼容旧版本!

对了,go test -race 测试不过。

@cs8425
Copy link
Author

cs8425 commented Mar 7, 2019

感謝提醒
已修正
除了基於net.Pipe()TestParallel2()加上-race還是不會過
把goroutine dump出來看還是找不到原因
全部都在select狀態...

找到問題點了
好像是net.Pipe()有buffer大小的限制
兩邊同時sendLoop()寫到一半剛好buffer滿了
而對面的recvLoop()又正在傳cmdACK cmdFUL cmdEMP不能清空buffer就會卡住

@cs8425
Copy link
Author

cs8425 commented Mar 9, 2019

懶的慢慢修
我從xtaci的master merge這邊重新提交一份PR好了

@cs8425 cs8425 closed this Mar 9, 2019
@cs8425 cs8425 mentioned this pull request Mar 9, 2019
@jannson
Copy link
Contributor

jannson commented Mar 9, 2019

感謝提醒
已修正
除了基於net.Pipe()TestParallel2()加上-race還是不會過
把goroutine dump出來看還是找不到原因
全部都在select狀態...

找到問題點了
好像是net.Pipe()有buffer大小的限制
兩邊同時sendLoop()寫到一半剛好buffer滿了
而對面的recvLoop()又正在傳cmdACK cmdFUL cmdEMP不能清空buffer就會卡住

那天晚上为了这个问题我测试到半夜 1 点,现在修复这个问题了?就是 TestParallel2 测试不过,其它改了是没问题的。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants