Closed cs8425 closed 5 years ago
你这样做最大的传输速度被限制在 16KB/RTT了
實測過並不會限制在16KB/RTT 設成16KB, 4MB, ping 12ms, 一樣跑到94Mbps(我這邊的頻寬上限100Mbps) 設成4KB, 4MB, 下降為70Mbps 只有stream read的速度<對面傳的速度才會受制於stream的buffer
你这个ping值太小,如果ping值一大,比如200ms,你速度就下来了
我去租一台ping 200ms的主機試試@@ 晚點再回報 不知道有沒有更好的模擬方法?
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
原本的:
$ 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
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
看了下,session.token你没有去掉, 总阀门没有关啊。 只是在当前的基础上,还增加了限速。
我明白你的意思了,你是想通过接收方发送控制信息,告诉发送方停止推流。
发送方必须要执行cmdFUL,cmdEMP进行阻塞。
这种方法的主要的问题就是可能造成限速, 最终 cmdFUL, cmdEMP会成为驱动下一次数据发送的时钟信号,这个时钟信号的间隔就是RTT。
你上面的测试是一个快速的consumer,可能并没有触发到cmdFUL/EMP。也就和原来一样的结果。
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一邊跑測速行嗎? 有沒有好一點的測試方法可以建議呢?
问题的核心在于,在什么条件下去通知发送方,阈值什么是合理的,16KB是拍脑袋?
为什么不是在 stream buffer 达到整体半窗(MaxReceiveBuffer/2)的时候.
好的
那我先解釋一下MaxStreamBuffer
跟MinStreamBuffer
的關係
一個有FIFO buffer的高速系統
一般設計上會用總大小的20%當(快要)空 80%當(快要)滿的時候傳送訊號
這樣不會浪費buffer也不會造成overflow
(這類的設計在MCU之類的硬體系統很常見, 有些會選30:70、25:75、10:90之類的)
16KB => 80%
4KB => 20%
當然更進階一點的就是設計一個簡單的估測系統(跟RTT、讀寫速度有關)
預估什麼時候送cmdFUL
不會造成overflow也不會剩下太多空間
什麼時候送cmdEMP
可以在buffer真的被讀到空之前收到新資料
至於MaxStreamBuffer
為何選16KB
我是隨手抓沒錯啦
不過還是有點道理的
假設一個(外部無瓶頸)高寫高讀的情況:
然而read/write的頻率是有上限的 我沒搞錯的話 在linux kernel跟Internal kernel timer frequency有關 是1000Hz (編譯kernel的時候能調整), 超過就要丟到kernel的buffer/queue 16KB * 1000Hz = 128Mbps ~= 16MB/s 我想應該沒有多少人的頻寬可以支撐N條速度超過128Mbps的stream吧...? 希望我這樣解釋夠清楚@@
也就是说16KB是经验值, 但是我说的一半也是有道理的, 在cmdFUL到达发送方的半个RTT时间内,剩下的来自发送方的数据流在这个时间刚好把buffer填满。
这样貌似解决的问题是慢速receiver导致卡住的问题,但没有解决快速sender+receiver抢占带宽的问题。 也就是流之间的QoS balance。这个问题存在根本的复杂性。
為何我不用選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的速度
大概這幾個速度要紀錄: 下載方向的讀寫、上傳方向的寫、上傳方向對面的讀(這個比較不準, 靠cmdFUL
、cmdEMP
回授而已, 可能需要在格式加入精確數值的欄位)
速度超標的就通知對面暫停寫入
自己這邊也可以暫停自己的寫入
詳細的頻寬分配方法/條件就要再思考思考了
可以考慮丟個handler function給上層應用來搞XD
另外我發現我命名好像不是那麼好
stream.markFUL()
換成stream.pauseWrite()
stream.markEMP()
換成stream.resumeWrite()
這樣應該會比較好理解
修改名字是可以的,另外,这个改动不兼容原来的代码,会造成session.go:273的退出,需要一个开关。
另外 CI 目前还过不了,你需要测试下:
- go test -coverprofile=coverage.txt -covermode=atomic -bench .
在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
预设应该同之前的版本保持一致,用法无需改动。
Merging #19 into master will increase coverage by
1.04%
. The diff coverage is94.89%
.
@@ 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% <ø> (ø) |
:arrow_up: |
session.go | 89.49% <100%> (+1.23%) |
:arrow_up: |
mux.go | 94.28% <77.77%> (-5.72%) |
:arrow_down: |
stream.go | 89.42% <95.38%> (+2.52%) |
:arrow_up: |
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.
带stream.token
原始
实测对于youtube播放,有明显的速度下降,应该就是stream流控所致。
youtube的视频流会应该会通过阻塞读取(暂停读取)来控制TCP推流速度。但加上stream.token的流控,确实又加了一层 cmdFUL/EMP的时钟,或者说类似于TCP_CORK的效果。
算是trade off吧 剛連線的時候的確無法迅速提高throughput 有個改法是剛連線的stream buffer給比較大 過了幾秒再調回正常(要幾秒可能要思考一下) 代價是一次開多個連線的時候記憶體使用量會瞬間飆高 等速度穩定之後才會降回去 雖然個人認為youtube播放只要跟的上不卡就行... 大大你覺得呢? 剛開的連線給先給比較大的stream buffer這個設計是否有必要?
我还是觉得,这个问题有根本的困难,内存总用量和流量平衡不可兼得。 可以继续探讨。
列一下幾個已知條件/現象:
那我試試看改個這種東西出來: 1.剛連線的stream buffer設為MaxReceiveBuffer/2 (或是除其他數, 儘量大但是不要卡住session) 2.觀察stream buffer使用量, 當前用量開始變小(只在0跟上次最大用量之間跳), 判定為進入穩定 3.進入穩定之後stream buffer重設成某個較小的數值(ex: 16KB) 理論上應該有用....晚點試試... RTT部份還在想要怎估測發送時機比較好
@xtaci
大大你再試試這個版本
加入了RTT測試(收到cmdNOP
回傳cmdACK
)
簡單估算cmdFUL
、cmdEMP
發送時機跟stream buffer應該給多少
用自己嚕的TCP over TCP跑youtube測試,
跟 c6b175f 比, 初始爬升快了不少, 最終速度高了大約25%
但是其他iperf之類的速度測試結果差不多就是了
重編了kcptun 其他測試參數跟上次一樣 不知道為啥今天比較快@@
重點在throughput變化的部份 原本的smux初期會衝到40Mbps左右, 然後再降回25Mbps左右 c6b175f 是一路慢慢爬到20Mbps左右測試就結束了 31ccec0 衝到25Mbps左右就維持在那個位置 上傳部份可能頻寬不夠大看不太出來
目前版本 31ccec0:
c6b175f:
原本的:
降速是无论如何也不能接受的,影响的用户量太大了。
31ccec0 並沒有降速啊 是直接爬到頻寬上限就穩定了 而原本的會超過頻寬上限之後才回歸正常 超過的部份就是靠記憶體再撐 所以 31ccec0 如果要達到一樣的效果 把buffer限制開大一點即可
我先观察一下
未来两周会有点忙,后面再回复,暂时没有时间验证这个,告知一下。
要不要另外編譯一版kcptun 開個issue特別著名一下是測試版 交給大家決定要不要當白老鼠&回報結果? 如果不行也沒關係 就等大大有空摟
不急,慢慢来,先思考一下。
抱歉晚回复,前几周工作上的事有点忙。
我始终觉得:
这三者最多同时满足两者。
大致上正確 第3點不是總量一定 應該說 RAM要小於 "最大吞吐量所需的RAM" x "stream數量" 而且3者之間的分配vs效果 愈接近極限 會愈沒有效果 假設最大吞吐量要 32kB / stream 95%的最大吞吐量可能只要 16kB / stream 如果RAM的使用量真的無法妥協 我會偏好降個幾%的最大吞吐量 來換(幾乎)所有stream不會卡住 畢竟...youtube點下去播了幾秒發現這不是想要的 點其他影片連結就整個卡住其實還蠻惱人的@@
@cs8425 有空更新一个最新版本么?感觉你说得有道理,我想测试测试。
@jannson 我直接merge了xtaci的v1.1.1到我的master 7f8c639 你可以先試試看效果 改的有點亂 要提交PR回這邊的話還要多花點功夫...
非常感谢,一回家就开始测试了,手动 merge 了一波。直观感觉效果挺好的~~~ 具体参数还没提供。 看了代码,可惜就是无法无缝兼容旧版本!
对了,go test -race 测试不过。
感謝提醒
已修正
除了基於net.Pipe()
的TestParallel2()
加上-race
還是不會過
把goroutine dump出來看還是找不到原因
全部都在select狀態...
找到問題點了
好像是net.Pipe()
有buffer大小的限制
兩邊同時sendLoop()
寫到一半剛好buffer滿了
而對面的recvLoop()
又正在傳cmdACK
cmdFUL
cmdEMP
不能清空buffer就會卡住
懶的慢慢修 我從xtaci的master merge這邊重新提交一份PR好了
感謝提醒 已修正 除了基於
net.Pipe()
的TestParallel2()
加上-race
還是不會過 把goroutine dump出來看還是找不到原因 全部都在select狀態...找到問題點了 好像是
net.Pipe()
有buffer大小的限制 兩邊同時sendLoop()
寫到一半剛好buffer滿了 而對面的recvLoop()
又正在傳cmdACK
cmdFUL
cmdEMP
不能清空buffer就會卡住
那天晚上为了这个问题我测试到半夜 1 点,现在修复这个问题了?就是 TestParallel2 测试不过,其它改了是没问题的。
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