skywind3000 / kcp

:zap: KCP - A Fast and Reliable ARQ Protocol
MIT License
15.36k stars 2.5k forks source link

KCP 测速结果 #202

Closed ZezhongWang closed 5 years ago

ZezhongWang commented 5 years ago

一下是对于 Kcp-go 的测速结果,但是我觉得C语言实现也会有同样的问题,想来请教一下。

我自己用go语言实现了iperf测速工具, 实现了基本的一些测速功能, 最主要的就是加上了对于自定义应用层协议的扩展, 所以也可以用来对kcp进行更精确地测速.因为 Fast.com 之类的还是涉及到了3个点的传输。

然后一个有趣的现象就是, 对于我自身的网络情况:

在上行情况下,用适当的参数,确实 KCP 的表现会比 TCP 好一些,大约有40%-50%提升 :

// KCP 结果, 平均带宽在 17.69Mb/s 左右
>.\iperf-go.exe -c <server_ip> -proto kcp -sw 512  // KCP 发送窗口512, 不开启FEC, 加密  
09:55:23.790 0 ▶ INFO 001 Go-logging init finish
Iperf started:
addr:104.224.137.244    port:5201       proto:rudp      interval:1000   duration:10     NoDelay:false   burst:true      BlockSize:4096  StreamNum:1
RUDP settting: sndWnd:512       rcvWnd:512      writeBufSize:4096Kb     readBufSize:4096Kb      noCongestion:true       flushInterval:10
Connect to server 104.224.137.244:5201 succeed.
[ ID]    Interval        Transfer        Bandwidth        RTT      Retrans   Retrans(%)  Lost(%)  Early Retrans  Fast Retrans
[  0] 0.00-1.00 sec      3.73 MB        29.81 Mb/s       165.0ms          11    0.39%   0.39%   0.00%   0.00%
[  0] 1.00-2.00 sec      2.47 MB        19.78 Mb/s       169.0ms         600    31.84%  22.50%  9.34%   0.00%
[  0] 2.00-3.00 sec      2.50 MB        20.03 Mb/s       166.0ms         356    18.66%  13.31%  5.35%   0.00%
[  0] 3.00-4.00 sec      1.85 MB        14.78 Mb/s       169.0ms         844    59.94%  44.67%  15.27%  0.00%
[  0] 4.00-5.00 sec      2.32 MB        18.56 Mb/s       165.0ms         606    34.27%  17.53%  16.74%  0.00%
[  0] 5.00-6.00 sec      1.54 MB        12.34 Mb/s       174.0ms         922    78.41%  48.14%  30.28%  0.00%
[  0] 6.00-7.00 sec      1.93 MB        15.41 Mb/s       199.0ms         852    58.06%  51.17%  6.88%   0.00%
[  0] 7.00-8.00 sec      1.69 MB        13.50 Mb/s       167.0ms         499    38.80%  18.97%  19.83%  0.00%
[  0] 8.00-9.00 sec      2.45 MB        19.56 Mb/s       183.0ms         842    45.19%  28.33%  16.85%  0.00%
[  0] 9.00-10.04 sec     1.64 MB        13.16 Mb/s       181.0ms         996    79.48%  39.98%  39.50%  0.00%
- - - - - - - - - - - - - - - - SUMMARY - - - - - - - - - - - - - - - -
[ ID]    Interval        Transfer        Bandwidth        RTT      Retrans   Retrans(%)  Lost(%)  Early Retrans  Fast Retrans
[  0] 0.00-10.04 sec    22.12 MB        17.69 Mb/s       173.8ms        6528    38.73%  25.03%  13.71%  0.00%   [SENDER]

// TCP 结果, 平均带宽在 12.10Mb/s 左右, 在发送端为windows系统的情况下拿不到 retrans 数据
>.\iperf-go.exe -c 104.224.137.244 -proto tcp  
09:56:23.161 0 ▶ INFO 001 Go-logging init finish
Iperf started:
addr:104.224.137.244    port:5201       proto:tcp       interval:1000   duration:10     NoDelay:false   burst:true      BlockSize:131072        StreamNum:1
Connect to server 104.224.137.244:5201 succeed.
[ ID]    Interval        Transfer        Bandwidth        RTT      Retrans   Retrans(%)
[  0] 0.00-1.00 sec      1.50 MB        12.00 Mb/s         0.0ms           0    0.00%
[  0] 1.00-2.00 sec      5.38 MB        43.00 Mb/s         0.0ms           0    0.00%
[  0] 2.00-3.01 sec      0.12 MB         1.00 Mb/s         0.0ms           0    0.00%
[  0] 3.01-4.00 sec      0.00 MB         0.00 Mb/s         0.0ms           0    NaN%
[  0] 4.00-5.00 sec      0.75 MB         6.00 Mb/s         0.0ms           0    0.00%
[  0] 5.00-6.00 sec      2.62 MB        21.00 Mb/s         0.0ms           0    0.00%
[  0] 6.00-7.00 sec      3.62 MB        29.00 Mb/s         0.0ms           0    0.00%
[  0] 7.00-8.01 sec      0.12 MB         1.00 Mb/s         0.0ms           0    0.00%
[  0] 8.01-9.00 sec      0.38 MB         3.00 Mb/s         0.0ms           0    0.00%
[  0] 9.00-10.27 sec     0.62 MB         5.00 Mb/s         0.0ms           0    0.00%
- - - - - - - - - - - - - - - - SUMMARY - - - - - - - - - - - - - - - -
[ ID]    Interval        Transfer        Bandwidth        RTT      Retrans   Retrans(%)
[  0] 0.00-10.27 sec    15.12 MB        12.10 Mb/s         NaNms           0    0.00%   [SENDER]

但是在下行的情况下(TCP有BBR支持),TCP的表现则好很多,快有 KCP 的一倍。并且我也调整过KCP的一些参数,均未获得较好的效果(例如调整snd_wnd, buffer之类)

// KCP 结果,大概在 8.05 Mb/s 左右(这还是经过几次尝试在比较好的结果下)

>.\iperf-go.exe -c 104.224.137.244 -proto rudp -sw 512 –R (客户端)
22:04:39.175 0 ▶ INFO 001 Go-logging init finish
Server listening on 5201
Accept connection from client: 221.4.34.225:42032
[ ID]    Interval        Transfer        Bandwidth        RTT      Retrans   Retrans(%)  Lost(%)  Early Retrans  Fast Retrans
[  0] 0.00-1.00 sec  1.23 MB     9.84 Mb/s   172.0ms     713    76.04%  7.47%   68.57%  0.00%
[  0] 1.00-2.00 sec  0.99 MB     7.91 Mb/s   172.0ms     956    126.94% 49.13%  77.81%  0.00%
[  0] 2.00-3.00 sec  0.96 MB     7.69 Mb/s   172.0ms     766    104.60% 21.99%  82.62%  0.00%
[  0] 3.00-4.00 sec  0.84 MB     6.75 Mb/s   176.0ms    1201    186.79% 65.94%  120.84% 0.00%
[  0] 4.00-5.00 sec  0.95 MB     7.59 Mb/s   174.0ms    1125    155.53% 33.87%  121.66% 0.00%
[  0] 5.00-6.00 sec  1.30 MB    10.44 Mb/s   174.0ms     849    85.39%  36.21%  49.18%  0.00%
[  0] 6.00-7.00 sec  1.09 MB     8.69 Mb/s   173.0ms     863    104.29% 48.34%  55.95%  0.00%
[  0] 7.00-8.00 sec  1.09 MB     8.75 Mb/s   172.0ms     818    98.14%  34.91%  63.23%  0.00%
[  0] 8.00-9.00 sec  0.47 MB     3.75 Mb/s   171.0ms     911    255.03% 106.10% 148.93% 0.00%
[  0] 9.00-10.27 sec     1.13 MB     9.06 Mb/s   180.0ms    1096    126.96% 65.10%  61.86%  0.00%
- - - - - - - - - - - - - - - - SUMMARY - - - - - - - - - - - - - - - -
[ ID]    Interval        Transfer        Bandwidth        RTT      Retrans   Retrans(%)  Lost(%)  Early Retrans  Fast Retrans
[  0] 0.00-10.27 sec    10.06 MB     8.05 Mb/s   173.6ms    9298    121.30% 42.56%  78.75%  0.00%   [SENDER]

// TCP BBR结果, 大概在20.40Mb/s左右
>.\iperf-go.exe -c 104.224.137.244 –R  (客户端)
22:04:17.793 0 ▶ INFO 001 Go-logging init finish
Server listening on 5201
Accept connection from client: 221.4.34.225:22567
[ ID]    Interval        Transfer        Bandwidth        RTT      Retrans   Retrans(%)
[  0] 0.00-1.00 sec  1.50 MB    12.00 Mb/s   172.6ms       0    0.00%
[  0] 1.00-2.00 sec  3.12 MB    25.00 Mb/s   169.1ms       0    0.00%
[  0] 2.00-3.00 sec  2.75 MB    22.00 Mb/s   205.3ms     378    19.14%
[  0] 3.00-4.00 sec  2.00 MB    16.00 Mb/s   164.8ms    1490    103.73%
[  0] 4.00-5.00 sec  4.75 MB    38.00 Mb/s   162.9ms     873    25.59%
[  0] 5.00-6.00 sec  1.25 MB    10.00 Mb/s   163.3ms       0    0.00%
[  0] 6.00-7.00 sec  2.50 MB    20.00 Mb/s   163.3ms       0    0.00%
[  0] 7.00-8.00 sec  2.62 MB    21.00 Mb/s   163.5ms       6    0.32%
[  0] 8.00-9.00 sec  2.38 MB    19.00 Mb/s   162.7ms       0    0.00%
[  0] 9.00-10.19 sec     2.50 MB    20.00 Mb/s   163.1ms       0    0.00%
- - - - - - - - - - - - - - - - SUMMARY - - - - - - - - - - - - - - - -
[ ID]    Interval        Transfer        Bandwidth        RTT      Retrans   Retrans(%)
[  0] 0.00-10.19 sec    25.50 MB    20.40 Mb/s   169.1ms    2747    0.15%   [SENDER]

通过其他参数可发现, KCP无论上行还是下行情况下丢包率都异常地高(30%,40%以上),再加上early resend的策略的话在下行的report中可以发现,重传的包量甚至已经超过了本身的包量(也就是说平均下来每个包都发了两次多)

这么来看在 UDP 上搭建可靠传输还是值得商榷的,这么高的丢包率可能与ISP对UDP流量的管制有关。

这里还想讨论的就是,

iperf-go 测试工具: https://github.com/ZezhongWang/iperf-go 欢迎使用!

skywind3000 commented 5 years ago

本项目开篇就说了,不是为流量设计的(每秒钟多少 KB),是为流速设计的(RTT)。

skywind3000 commented 5 years ago

完整的 udp 传输系统有很多需要注意的地方,以其你自己写 kcp 实现,不如去测试下 kcptun

ZezhongWang commented 5 years ago

kcptun 就是用 kcp 实现的, kcp都快不了更不用说 kcptun。

然后关于 RTT 这点也是有问题的。 加上了 ackNodelay,fastRetrans 这些策略确实可能原理上是能更稍微有一些提升,但是 30%-40% 的RTT的提升这个数据是哪里来的??

RTT 本身很大程度上是取决于本身网络链路状态,类似于网络的一个基本属性。测试数据也显示并不能有什么提升。

caoli5288 commented 5 years ago

我比较对你实现的bbr拥塞控制有兴趣,有patch吗

ZezhongWang commented 5 years ago

我比较对你实现的bbr拥塞控制有兴趣,有patch吗

现在实现的还比较粗略。其实就是在kcp-go的基础上加了个类似于 tcp_bbr.c 中的 model,然后再在收到ack 的时候update model,目前是自己开了个 private 写了测试下,因为直接用的别人的代码而且写的有点随意就没 public

skywind3000 commented 5 years ago

我举个例子,一个运动员专业是跑步,你的测试方法是叫他去跟人比赛游泳。

为什么你上行变快了,下行却没有?

还是老话,KCP 优化的是 Latency/RTT (XX ms),所以你测试的是带宽(KB/s)属于 ”间接测试“,并不是 ”直接测试“。KCP 设计目的是比 TCP 更低的 Latency/RTT,而不是更好的带宽利用率或者 KB/s,那么:

  1. 当你的传输速率接近物理带宽极限时,由于 TCP 带宽利用率更充分,所以 TCP 会更快(KB/s)。
  2. 当你的传输没有到物理带宽极限时,当有丢包发生时,KCP 会更快(KB/s)。
  3. 当你的传输没有到物理带宽极限时,当没有丢包发生时,两个一样快(KB/s)。

那么为什么经常有用 kcp 加速 VPS 翻墙和音视频推流呢?因为一般你上一下 youtube 或者传递下音视频流,带宽远没有达到物理上限(不是你这种 iperf 要榨干网络的传输法),比如公网高峰期 5%-10%的丢包的时候,远距离传输的时候,此时物理带宽的上限远没达到,但是因为延迟和丢包率的存在,导致 tcp 的 latency 极高,KB/s 也上不去,这也是最常见的情况,此时 KCP 的加速效果明显,不管对 Latency 和 KB/s。

理解这个原理你就不会用榨干网络带宽的方式来测试 KB/s 了。

RTT 30%, 40% 的降低哪里来的?

真实产品 100 多万用户同时在线测试出来的,模拟丢包测试出来的,取个平均值。

我自己应用过 KCP 的项目,基本都测试过: https://github.com/skywind3000/kcp/wiki/HISTORY

不止我一个人测试,数个团队分别不同层面测试,结论相同。

RTT 本身很大程度上是取决于本身网络链路状态,类似于网络的一个基本属性。测试数据也显示并不能有什么提升。

你说的 RTT 是物理级别 UDP / ICMP 的 round-trip-time,我指的 RTT 是数据经过 TCP/KCP 之类可靠协议传输走一圈的时间,只要没有触碰到物理带宽上限,当然会比 TCP 快很多,这个数值的测试,KCP 首页末尾也提到了很多,他们的程序和测试环境描述都放在那里,你感兴趣自己去看。

test.cpp 也模拟了不同延迟和丢包情况下,这些策略的开关到底能带来多大的提升。这么明显的区别你但凡看过一下都不会说出 “很大程度上是取决于本身网络链路状态” 这种话来。

为了准确交流,RTT 指代网络物理层的 udp/icmp ping 值,而 Latency 指数据经过可靠协议传输后走一圈的延迟。标准 Latency 测试要怎么测呢?不是简单弄个 KB/s,你先要画一张表:

丢包/延迟 10ms 50ms 100ms 200ms
0% .. .. .. ..
5% .. .. .. ..
10% .. .. .. ..
15% .. .. .. ..
20% .. .. .. ..

然后针对每一种情况,计算出一个平均延迟来,比如下面这张图,就是网络延迟 300 毫秒,丢包率 20% 的情况,多种不同的协议传输延迟(Latency)分布图:

benchmark

横坐标是延迟,纵坐标是该协议有百分之多少的样本延迟小于等于横坐标代表的值。

举个例子:

而青色方块(P4),是未经任何可靠协议处理的,裸 UDP RTT 时间,你可以理解成网络 RTT 的物理下限,协议做的好,就是无限制的靠近这条青色方块线,协议做的差就会远离。

在上面这个图中,就所有样本的平均延迟而言,KCP 472ms 同时 TCP 是 698ms,而最大延迟,KCP 比 TCP 低很多倍(记不得了)。

对每种不同网络情况,都做这么一张图,都得到一个平均延迟和最大延迟,填到上面的表上去,30%-40%就是这么测试出来的,搞明白了么?

ZezhongWang commented 5 years ago

你说的数据统计确实很多paper中也是这么做的。不过这么做我还有一个问题。

一切都是基于: 在所有被测试协议在相同的网络丢包率相同的情况下

但是实际网络情况并不是这样的,就比如说,在相同的网络, TCP 和 UDP 以相同的网络速率(例如 1MB/s)发送数据, 实际网络中UDP 丢包率明显会比 TCP 大

比如我以 1MB/s 速率发送数据:

// UDP 发包丢包率为 3.30%
>.\iperf-go.exe -c 104.224.137.244 -b 1 -proto kcp -sw 128
10:31:20.612 0 ▶ INFO 001 Go-logging init finish
Iperf started:
addr:104.224.137.244    port:5201       proto:rudp      interval:1000   duration:10     NoDelay:false   burst:false     BlockSize:4096  StreamNum:1
RUDP settting: sndWnd:128       rcvWnd:512      writeBufSize:4096Kb     readBufSize:4096Kb      noCongestion:true       flushInterval:10        dataShards:0    parityShards:0
Connect to server 104.224.137.244:5201 succeed.
[ ID]    Interval        Transfer        Bandwidth        RTT        Retrans   Retrans(%)  Lost(%)  Early(%)  Fast(%)
[  0] 0.00-1.00 sec      0.98 MB         7.88 Mb/s       164.0ms          26    3.35%   3.35%   0.00%   0.00%
[  0] 1.00-2.00 sec      0.98 MB         7.81 Mb/s       164.0ms          15    1.98%   1.98%   0.00%   0.00%
[  0] 2.00-3.00 sec      0.97 MB         7.78 Mb/s       164.0ms          30    3.89%   3.89%   0.00%   0.00%
[  0] 3.00-4.03 sec      0.93 MB         7.44 Mb/s       174.0ms          25    3.41%   3.41%   0.00%   0.00%
[  0] 4.03-5.00 sec      0.98 MB         7.88 Mb/s       165.0ms          66    8.09%   8.09%   0.00%   0.00%
[  0] 5.00-6.00 sec      0.95 MB         7.62 Mb/s       166.0ms          13    1.76%   1.76%   0.00%   0.00%
[  0] 6.00-7.00 sec      0.97 MB         7.75 Mb/s       165.0ms          35    4.53%   4.53%   0.00%   0.00%
[  0] 7.00-8.00 sec      0.96 MB         7.69 Mb/s       164.0ms           4    0.54%   0.54%   0.00%   0.00%
[  0] 8.00-9.00 sec      0.99 MB         7.94 Mb/s       165.0ms          24    3.08%   3.08%   0.00%   0.00%
[  0] 9.00-10.00 sec     0.96 MB         7.72 Mb/s       164.0ms          16    2.13%   2.13%   0.00%   0.00%
- - - - - - - - - - - - - - - - SUMMARY - - - - - - - - - - - - - - - -
[ ID]    Interval        Transfer        Bandwidth        RTT        Retrans   Retrans(%)  Lost(%)  Early(%)  Fast(%)  Recover(%)  PktsLost(%)  SegsLost(%)
[  0] 0.00-10.00 sec     9.69 MB         7.75 Mb/s       165.5ms         254    3.30%   3.30%   0.00%   0.00%   0.00%   0.03%   0.03%   [SENDER]

// tcp 丢包率为0
>.\iperf-go.exe -c 104.224.137.244 -b 1
10:30:49.166 0 ▶ INFO 001 Go-logging init finish
Iperf started:
addr:104.224.137.244    port:5201       proto:tcp       interval:1000   duration:10     NoDelay:false   burst:false     BlockSize:131072        StreamNum:1
Connect to server 104.224.137.244:5201 succeed.
[ ID]    Interval        Transfer        Bandwidth        RTT        Retrans
[  0] 0.00-1.00 sec      1.00 MB         8.00 Mb/s         0.0ms           0
[  0] 1.00-2.00 sec      1.00 MB         8.00 Mb/s         0.0ms           0
[  0] 2.00-3.00 sec      1.00 MB         8.00 Mb/s         0.0ms           0
[  0] 3.00-4.00 sec      1.00 MB         8.00 Mb/s         0.0ms           0
[  0] 4.00-5.00 sec      1.00 MB         8.00 Mb/s         0.0ms           0
[  0] 5.00-6.00 sec      1.00 MB         8.00 Mb/s         0.0ms           0
[  0] 6.00-7.00 sec      1.00 MB         8.00 Mb/s         0.0ms           0
[  0] 7.00-8.00 sec      1.00 MB         8.00 Mb/s         0.0ms           0
[  0] 8.00-9.00 sec      1.00 MB         8.00 Mb/s         0.0ms           0
[  0] 9.00-10.00 sec     1.12 MB         9.00 Mb/s         0.0ms           0
- - - - - - - - - - - - - - - - SUMMARY - - - - - - - - - - - - - - - -
[ ID]    Interval        Transfer        Bandwidth        RTT        Retrans   Retrans(%)
[  0] 0.00-10.00 sec    10.12 MB         8.10 Mb/s         NaNms           0    0.00%   [SENDER]

目前根据我已知的,这种丢包率的差距跟 ISP 对UDP流量的管控有关(还可能有别的影响因素) 也就是说,在真实的网络中,你通过在网络模拟器(NS)模拟出来的效果并不是像你通过在设定的 ”网络延迟 300 毫秒,丢包率 20% 的情况“ 跑的那么好。

换取话说,如果要真正比较在实际网络中的性能, 丢包率应该在 TCP 与 UDP 之间进行换算(不过这种换算方法目前我也没搞清楚),假设UDP平均丢包率是TCP的两倍(实际情况在触及bottleneck的时候会远大于这个值),那么此时表格就应该变成:

TCP丢包/UDP丢包/延迟 10ms 50ms 100ms 200ms
0%/0% .. .. .. ..
5%/10% .. .. .. ..
10%/20% .. .. .. ..
15%/30% .. .. .. ..
20%/40% .. .. .. ..

所以我认为如果只是单纯地在 NS 上进行数据统计,也应该将丢包率进行换算,否则就只是理想状态下的一个统计。

ZezhongWang commented 5 years ago

讨论归讨论(我还是得表达一下对大神的敬意,毕竟我现在是还写不出这样的代码

skywind3000 commented 5 years ago

存在运营商UDP限流,但是大部分应用,比如传点视频这类,远达不到限流的限制。Retrans 衡量丢包率是不对的,因为这是协议算出来的,协议本来就更倾向于判断为:丢包。

30%-40%的latency降低不光是内网模拟丢包实验的结果,更是外网将用户分组,同一时段两组用户用不同的协议对比出来的。

你的问题主要是测试方法从一开始就是间接测试,而不是直接测试,另外是想象成份过大。

就这么着吧,能理解就理解,不能理解就做罢。

我也没时间给你免费讲课了,忙。

ZezhongWang commented 5 years ago

收到了,我的测试方法确实存在一些问题,感谢大佬抽出时间答疑

xylophone21 commented 11 months ago

请问一下这周图里的丢包是上或下行丢包20%, 还是上下行丢包各20%(综合丢包的话,就是36%了?)