为了账号安全,请及时绑定邮箱和手机立即绑定

为什么 Go 套接字比 C++ 套接字慢?

为什么 Go 套接字比 C++ 套接字慢?

Go
扬帆大鱼 2023-05-22 16:00:37
我在 Go 和 C++ 中对一个简单的套接字乒乓测试进行了基准测试。客户端首先向服务器发送 0。服务器递增它获得的任何数字并将其发送回客户端。客户端将数字回显给服务器,并在数字达到 1,000,000 时停止。客户端和服务器都在同一台计算机上,所以我在这两种情况下都使用 Unix 套接字。(我还尝试了同主机 TCP 套接字,显示了类似的结果)。Go 测试需要 14 秒,而 C++ 测试需要 8 秒。这让我感到惊讶,因为我已经运行了相当多的 Go 与 C++ 基准测试,并且通常只要我不触发垃圾收集器,Go 的性能就与 C++ 一样。我在 Mac 上,尽管评论者也报告说 Go 版本在 Linux 上运行速度较慢。想知道我是否缺少一种优化 Go 程序的方法,或者是否只是底层效率低下。下面是我运行的用于执行测试的命令以及测试结果。所有代码文件都粘贴在这个问题的底部。运行围棋服务器:$ rm /tmp/go.sock$ go run socketUnixServer.go运行 Go 客户端:$ go build socketUnixClient.go; time ./socketUnixClientreal    0m14.101suser    0m5.242ssys     0m7.883s运行 C++ 服务器:$ rm /tmp/cpp.sock$ clang++ -std=c++11 tcpServerIncUnix.cpp -O3; ./a.out运行 C++ 客户端:$ clang++ -std=c++11 tcpClientIncUnix.cpp -O3; time ./a.outreal    0m8.690suser    0m0.835ssys     0m3.800s代码文件去服务器:// socketUnixServer.gopackage mainimport (    "log"    "net"    "encoding/binary")func main() {    ln, err := net.Listen("unix", "/tmp/go.sock")    if err != nil {        log.Fatal("Listen error: ", err)    }    c, err := ln.Accept()    if err != nil {        panic(err)    }    log.Println("Connected with client!")    readbuf := make([]byte, 4)    writebuf := make([]byte, 4)    for {        c.Read(readbuf)        clientNum := binary.BigEndian.Uint32(readbuf)        binary.BigEndian.PutUint32(writebuf, clientNum+1)        c.Write(writebuf)    }}去客户端:// socketUnixClient.gopackage mainimport (    "log"    "net"    "encoding/binary")const N = 1000000func main() {    c, err := net.Dial("unix", "/tmp/go.sock")    if err != nil {        log.Fatal("Dial error", err)    }    defer c.Close()    readbuf := make([]byte, 4)    writebuf := make([]byte, 4)    var currNumber uint32 = 0    for currNumber < N {        binary.BigEndian.PutUint32(writebuf, currNumber)        c.Write(writebuf)        // Read the incremented number from server        c.Read(readbuf[:])        currNumber = binary.BigEndian.Uint32(readbuf)    }}
查看完整描述

1 回答

?
holdtom

TA贡献1805条经验 获得超10个赞

首先,我确认这个问题中的 Go 程序确实比 C++ 程序运行得明显慢。我认为知道原因确实很有趣。

我用 分析了 Go 客户端和服务器,pprof发现这syscall.Syscall占用了总执行时间的 70%。根据这张票,在 Go 中系统调用比在 C 中慢大约 1.4 倍。

(pprof) top -cum

Showing nodes accounting for 18.78s, 67.97% of 27.63s total

Dropped 44 nodes (cum <= 0.14s)

Showing top 10 nodes out of 44

  flat  flat%   sum%        cum   cum%

 0.11s   0.4%   0.4%     22.65s 81.98%  main.main

     0     0%   0.4%     22.65s 81.98%  runtime.main

18.14s 65.65% 66.05%     19.91s 72.06%  syscall.Syscall

 0.03s  0.11% 66.16%     12.91s 46.72%  net.(*conn).Read

 0.10s  0.36% 66.52%     12.88s 46.62%  net.(*netFD).Read

 0.16s  0.58% 67.10%     12.78s 46.25%  internal/poll.(*FD).Read

 0.06s  0.22% 67.32%     11.87s 42.96%  syscall.Read

 0.11s   0.4% 67.72%     11.81s 42.74%  syscall.read

 0.02s 0.072% 67.79%      9.30s 33.66%  net.(*conn).Write

 0.05s  0.18% 67.97%      9.28s 33.59%  net.(*netFD).Write

Conn.Write我逐渐减少和调用的次数Conn.Read并相应地增加缓冲区的大小,以便传输的字节数保持不变。结果是程序进行的这些调用越少,其性能就越接近 C++ 版本。


查看完整回答
反对 回复 2023-05-22
  • 1 回答
  • 0 关注
  • 69 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信