GO的TCP性能测试,优化结果

GO的TCP性能测试,优化结果
这次针对TCP部分对go做了优化,测试结果令⼈满意。GO单进程(7Gbps)不输c++(8Gbps),是c++使⽤writev(16Gbps)的⼀半,GO多进程(59Gbps)完胜c++是c++的好⼏倍。
备注:之前的测试是在虚拟机上,这次在物理机上,结果可能会略有不同。
这次使⽤的GO的版本是1.3.3。
Why TCP
TCP是⽹络通讯的基础,⽽web则是基于HTTP框架,HTTP⼜基于TCP。RTMP也是基于TCP。
TCP的吞吐率能达标,那么就奠定了这个语⾔能开发⾼性能服务器的基础。
之前srs1.0时,调研过go,写了⼀个go版本的srs,但是性能和red5差不多就删除了。
现在srs2.0单进程单线程⽹络吞吐能达到4Gbps(6千客户端,码率522Kbps),GO如果不能达到这个⽬标,那么SRS就不可能⽤go重写。
Platform
此次测试在24CPU的服务器上,CPU不是瓶颈。
测试使⽤lo⽹卡,直接内存拷贝,⽹络也不是瓶颈。
CPU和⽹络都资源充⾜时,服务器本⾝的执⾏速度就是关键了。
OS选⽤Centos6 64bits。
客户端选⽤c++做客户端,使⽤同⼀个客户端测试。
Write
下⾯是C++服务器,单进程单线程作为服务器:
g++ tcp.server.cpp -g -O0 -o tcp.server && ./tcp.server 1990 4096
g++ tcp.client.cpp -g -O0 -o tcp.client && ./tcp.client 127.0.0.1 1990 4096
----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in  out | int  csw
0  6  92  0  0  1|  0    11k|1073M 1073M|  0    0 |2790    81k
0  6  93  0  0  1|  0  7782B|1049M 1049M|  0    0 |2536    76k
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
32573 winlin    20  0 11744  892  756 R 99.9  0.0  17:56.88 ./tcp.server 1990 4096
2880 winlin    20  0 11740  900  764 S 85.3  0.0  0:32.53 ./tcp.client 127.0.0.1 1990 4096
单进程的c++效率还是⾮常⾼的1049MBps,对⽐⽬前SRS1的168MBps,SRS2跑到的391MBps,其实SRS没有达到性能极限。
下⾯是go作为服务器,no delay设置为1,即go默认的tcp选项,这个选项会造成tcp性能降低:
go build ./ && ./tcp.server 1 1 1990 4096
g++ tcp.client.cpp -g -O0 -o tcp.client && ./tcp.client 127.0.0.1 1990 4096
----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in  out | int  csw
0  5  93  0  0  2|  0  7509B| 587M  587M|  0    0 |2544  141k
0  5  93  0  0  2|  0    10k| 524M  524M|  0    0 |2629  123k
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
5496 winlin    20  0 98248 1968 1360 S 100.5  0.0  4:40.54 ./tcp.server 1 1 1990 4096
5517 winlin    20  0 11740  896  764 S 72.3  0.0  3:24.22 ./tcp.client 127.0.0.1 1990 4096
可见在开启tcp no delay的go只有c++的性能的⼀半。
下⾯是go作为服务器,关闭了tcp no delay选项,单进程作为服务器:
go build ./ && ./tcp.server 1 0 1990 4096
g++ tcp.client.cpp -g -O0 -o tcp.client && ./tcp.client 127.0.0.1 1990 4096
----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in  out | int  csw
0  5  93  0  0  1|  0    10k| 868M  868M|  0    0 |2674    79k
1  5  93  0  0  1|  0    16k| 957M  957M|  0    0 |2660    85k
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
3004 winlin    20  0 98248 1968 1360 R 100.2  0.0  2:27.32 ./tcp.server 1 0 1990 4096
3030 winlin    20  0 11740  900  764 R 81.0  0.0  1:59.42 ./tcp.client 127.0.0.1 1990 4096
其实在关闭了tcp no delay之后,go的性能和c++相差不⼤了。
Multiple CPU
go最厉害的是考虑多cpu并⾏计算。其实c/c++也可以⽤fork多进程,对于业务代码有很⼤影响;go在不影响业务代码时直接扩展⽀持多cpu。同志空间
go开启10个cpu,8个客户端,开启no delay(默认)时的性能:
go build ./ && ./tcp.server 10 1 1990 4096
g++ tcp.client.cpp -g -O0 -o tcp.client && for((i=0;i<8;i++)); do (./tcp.client 127.0.0.1 1990 4096 &); done
----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in  out | int  csw
4  37  47  0  0  12|  0  105k|3972M 3972M|  0    0 |  14k  995k
4  37  46  0  0  13|  0  8055B|3761M 3761M|  0    0 |  14k  949k
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
6353 winlin    20  0  517m 6896 1372 R 789.6  0.0  13:24.49 ./tcp.server 10 1 1990 4096
6384 winlin    20  0 11740  900  764 S 68.4  0.0  1:11.57 ./tcp.client 127.0.0.1 1990 4096
6386 winlin    20  0 11740  896  764 R 67.4  0.0  1:09.53 ./tcp.client 127.0.0.1 1990 4096
6390 winlin    20  0 11740  900  764 R 66.7  0.0  1:11.24 ./tcp.client 127.0.0.1 1990 4096
6382 winlin    20  0 11740  896  764 R 64.8  0.0  1:11.30 ./tcp.client 127.0.0.1 1990 4096
6388 winlin    20  0 11740  896  764 R 64.4  0.0  1:11.80 ./tcp.client 127.0.0.1 1990 4096
6380 winlin    20  0 11740  896  764 S 63.4  0.0  1:08.78 ./tcp.client 127.0.0.1 1990 4096
6396 winlin    20  0 11740  896  764 R 62.8  0.0  1:09.47 ./tcp.client 127.0.0.1 1990 4096
蛋白酶体6393 winlin    20  0 11740  900  764 R 61.4  0.0  1:11.90 ./tcp.client 127.0.0.1 1990 4096
也⽐较厉害了,能跑到30Gbps。
开启10个cpu,8个客户端,关闭no delay时看看go的性能:
康复舞春满人间
go build ./ && ./tcp.server 10 0 1990 4096
g++ tcp.client.cpp -g -O0 -o tcp.client && for((i=0;i<8;i++)); do (./tcp.client 127.0.0.1 1990 4096 &); done
----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in  out | int  csw
5  42  41  0  0  12|  0  8602B|7132M 7132M|  0    0 |  15k  602k
5  41  41  0  0  12|  0    13k|7426M 7426M|  0    0 |  15k  651k
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
4148 winlin    20  0  528m 9.8m 1376 R 795.5  0.1  81:48.12 ./tcp.server 10 0 1990 4096
4167 winlin    20  0 11740  896  764 S 89.8  0.0  8:16.52 ./tcp.client 127.0.0.1 1990 4096
4161 winlin    20  0 11740  900  764 R 87.8  0.0  8:14.63 ./tcp.client 127.0.0.1 1990 4096
4174 winlin    20  0 11740  896  764 S 83.2  0.0  8:09.40 ./tcp.client 127.0.0.1 1990 4096
4163 winlin    20  0 11740  896  764 R 82.6  0.0  8:07.80 ./tcp.client 127.0.0.1 1990 4096
4171 winlin    20  0 11740  900  764 R 82.2  0.0  8:08.75 ./tcp.client 127.0.0.1 1990 4096
4169 winlin    20  0 11740  900  764 S 81.9  0.0  8:15.37 ./tcp.client 127.0.0.1 1990 4096
4165 winlin    20  0 11740  900  764 R 78.9  0.0  8:09.98 ./tcp.client 127.0.0.1 1990 4096
止水帷幕
4177 winlin    20  0 11740  900  764 R 74.0  0.0  8:07.63 ./tcp.client 127.0.0.1 1990 4096
这个更厉害了,能跑到59Gbps,厉害!
Writev
GO中是没有writev的,可以⽤c/c++的writev和go的多进程⽐⼀⽐。
考虑SRS2⽬前使⽤writev提升了⼀倍性能,若srs使⽤go则可以使⽤多进程,这两个对⽐还是有意义的。同时,客户端也使⽤readv来⼀次读取多次,弥补客户端单进程的瓶颈。
g++ tcp.server.writev.cpp -g -O0 -o tcp.server && ./tcp.server 64 1990 4096
g++ adv.cpp -g -O0 -o tcp.client && ./tcp.client 127.0.0.1 1990 64 4096
----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in  out | int  csw
0  6  93  0  0  1|  0    15k|1742M 1742M|  0    0 |2578    30k
北京雪亮眼镜店0  6  93  0  0  1|  0    13k|1779M 1779M|  0    0 |2412    30k
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
9468 winlin    20  0 12008 1192  800 R 99.8  0.0  1:17.63 ./tcp.server 64 1990 4096
9487 winlin    20  0 12008 1192  800 R 80.3  0.0  1:02.49 ./tcp.client 127.0.0.1 1990 64 4096
使⽤writev确实能提升⼀倍的性能,减少了系统调⽤的时间。
对⽐使⽤readv客户端的go,禁⽤tcp no delay的情况:
go build ./ && ./tcp.server 1 0 1990 4096
g++ adv.cpp -g -O0 -o tcp.client && ./tcp.client 127.0.0.1 1990 64 4096
----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in  out | int  csw
0  5  93  0  0  1|  0  5734B| 891M  891M|  0    0 |2601  101k
0  5  93  0  0  2|  0  9830B| 897M  897M|  0    0 |2518  103k
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
9690 winlin    20  0 98248 3984 1360 R 100.2  0.0  2:46.84 ./tcp.server 1 0 1990 4096
9698 winlin    20  0 12008 1192  800 R 79.3  0.0  2:13.23 ./tcp.client 127.0.0.1 1990 64 4096
这时候go单进程没有提升,因为瓶颈不在客户端,所以客户端使⽤readv之后也没有改变。
对⽐go⽤10个cpu,客户端使⽤readv,禁⽤tcp no delay:
go build ./ && ./tcp.server 10 0 1990 4096
g++ adv.cpp -g -O0 -o tcp.client && for((i=0;i<8;i++)); do (./tcp.client 127.0.0.1 1990 64 4096 &); done
----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
宋逸婷usr sys idl wai hiq siq| read  writ| recv  send|  in  out | int  csw
5  41  42  0  0  12|  0  7236B|6872M 6872M|  0    0 |  15k  780k
4  42  41  0  0  12|  0  9557B|6677M 6677M|  0    0 |  15k  723k
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
10169 winlin    20  0  655m 7072 1388 R 799.9  0.0  51:39.13 ./tcp.server 10 0 1990 4096
10253 winlin    20  0 12008 1192  800 R 84.5  0.0  5:05.05 ./tcp.client 127.0.0.1 1990 64 4096
10261 winlin    20  0 12008 1192  800 S 80.6  0.0  5:04.77 ./tcp.client 127.0.0.1 1990 64 4096
10255 winlin    20  0 12008 1192  800 R 79.9  0.0  5:05.32 ./tcp.client 127.0.0.1 1990 64 4096
10271 winlin    20  0 12008 1192  800 S 79.3  0.0  5:05.15 ./tcp.client 127.0.0.1 1990 64 4096
10258 winlin    20  0 12008 1192  800 S 78.3  0.0  5:05.45 ./tcp.client 127.0.0.1 1990 64 4096
10268 winlin    20  0 12008 1192  800 R 77.6  0.0  5:06.54 ./tcp.client 127.0.0.1 1990 64 4096
10251 winlin    20  0 12008 1188  800 R 76.6  0.0  5:03.68 ./tcp.client 127.0.0.1 1990 64 4096
10265 winlin    20  0 12008 1192  800 R 74.6  0.0  5:03.35 ./tcp.client 127.0.0.1 1990 64 4096
测试结果和之前差不多。
GO Write Analysis
调试go的TcpConn.Write⽅法,调⽤堆栈是:
at /home/winlin/go/src/github//research/tcp/:203
203        n, err := conn.Write(b)
调⽤的代码是:
func handleConnection(conn *net.TCPConn, no_delay int, packet_bytes int) {
for {
n, err := conn.Write(b)
if err != nil {
fmt.Println("write data error, n is", n, "and err is", err)
break
}
}
s调试进去,调⽤的是:
net.(*conn).Write (c=0xc20805bf18, b=..., ~r1=0, ~r2=...) at /usr/local/go/src/pkg/:130
130  return c.fd.Write(b)
这部分的代码是:
func (c *conn) Write(b []byte) (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
}
return c.fd.Write(b)
}
接下来是:
Breakpoint 2, net.(*netFD).Write (fd=0x0, p=..., nn=0, err=...) at /usr/local/go/src/pkg/net/:327 327  n, err = syscall.Write(int(fd.sysfd), p[nn:])
这部分代码是:
func (fd *netFD) Write(p []byte) (nn int, err error) {
if err := fd.writeLock(); err != nil {
return 0, err
}
defer fd.writeUnlock()
if err := fd.pd.PrepareWrite(); err != nil {
return 0, &OpError{"write", fd, fd.raddr, err}
}
for {
var n int
n, err = syscall.Write(int(fd.sysfd), p[nn:])
if n > 0 {
nn += n
}
if nn == len(p) {
break
}
if err == syscall.EAGAIN {
if err = fd.pd.WaitWrite(); err == nil {
continue
}
}
if err != nil {
n = 0
break
}
if n == 0 {
err = io.ErrUnexpectedEOF
break
}
}
if err != nil {
err = &OpError{"write", fd, fd.raddr, err}
}
return nn, err
}
系统调⽤层是:
syscall.Write (fd=0, p=..., n=0, err=...) at /usr/local/go/src/pkg/syscall/:152
152  n, err = write(fd, p)

本文发布于:2024-09-21 20:52:29,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/230859.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:性能   进程   客户端   结果   服务器   测试   作为
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议