猿问

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

我在 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 ./socketUnixClient


real    0m14.101s

user    0m5.242s

sys     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.out


real    0m8.690s

user    0m0.835s

sys     0m3.800s

代码文件


去服务器:


// socketUnixServer.go


package main


import (

    "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.go


package main


import (

    "log"

    "net"

    "encoding/binary"

)


const N = 1000000


func 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)

    }

}


扬帆大鱼
浏览 102回答 1
1回答

holdtom

首先,我确认这个问题中的 Go 程序确实比 C++ 程序运行得明显慢。我认为知道原因确实很有趣。我用 分析了 Go 客户端和服务器,pprof发现这syscall.Syscall占用了总执行时间的 70%。根据这张票,在 Go 中系统调用比在 C 中慢大约 1.4 倍。(pprof) top -cumShowing nodes accounting for 18.78s, 67.97% of 27.63s totalDropped 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.main18.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).WriteConn.Write我逐渐减少和调用的次数Conn.Read并相应地增加缓冲区的大小,以便传输的字节数保持不变。结果是程序进行的这些调用越少,其性能就越接近 C++ 版本。
随时随地看视频慕课网APP

相关分类

Go
我要回答