我对Go和C++进行了一个简单的socket ping pong测试基准。客户端首先向服务器发送0。服务器将收到的任何数字加1并将其发送回客户端。客户端将数字回显到服务器,并在数字达到100万时停止。
由于客户端和服务器都在同一台计算机上,因此我在两种情况下都使用Unix套接字。(我还尝试过同一主机的TCP套接字,结果显示类似)。
Go测试需要14秒,而C++测试需要8秒。这让我很惊讶,因为我已经运行了相当数量的Go vs. C++基准测试,并且一般来说,只要我不触发垃圾收集器,Go就与C++一样具有高性能。
虽然评论者也报告说在Linux上Go版本较慢,但我使用的是Mac。
想知道我是否错过了优化Go程序的方法,或者底层存在低效率。
以下是我运行测试的命令以及测试结果。所有代码文件都粘贴在本问题的底部。
运行Go服务器:
运行C++服务器:
由于客户端和服务器都在同一台计算机上,因此我在两种情况下都使用Unix套接字。(我还尝试过同一主机的TCP套接字,结果显示类似)。
Go测试需要14秒,而C++测试需要8秒。这让我很惊讶,因为我已经运行了相当数量的Go vs. C++基准测试,并且一般来说,只要我不触发垃圾收集器,Go就与C++一样具有高性能。
虽然评论者也报告说在Linux上Go版本较慢,但我使用的是Mac。
想知道我是否错过了优化Go程序的方法,或者底层存在低效率。
以下是我运行测试的命令以及测试结果。所有代码文件都粘贴在本问题的底部。
运行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
代码文件
Go 服务器:
// 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)
}
}
Go客户端:
// 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)
}
}
C++ 服务器:
// tcpServerIncUnix.cpp
// Server side C/C++ program to demonstrate Socket programming
// #include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <unistd.h>
// Big Endian (network order)
unsigned int fromBytes(unsigned char b[4]) {
return b[3] | b[2]<<8 | b[1]<<16 | b[0]<<24;
}
void toBytes(unsigned int x, unsigned char (&b)[4]) {
b[3] = x;
b[2] = x>>8;
b[1] = x>>16;
b[0] = x>>24;
}
int main(int argc, char const *argv[])
{
int server_fd, new_socket, valread;
struct sockaddr_un saddr;
int saddrlen = sizeof(saddr);
unsigned char recv_buffer[4] = {0};
unsigned char send_buffer[4] = {0};
server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
saddr.sun_family = AF_UNIX;
strncpy(saddr.sun_path, "/tmp/cpp.sock", sizeof(saddr.sun_path));
saddr.sun_path[sizeof(saddr.sun_path)-1] = '\0';
bind(server_fd, (struct sockaddr *)&saddr, sizeof(saddr));
listen(server_fd, 3);
// Accept one client connection
new_socket = accept(server_fd, (struct sockaddr *)&saddr, (socklen_t*)&saddrlen);
printf("Connected with client!\n");
// Note: if /tmp/cpp.sock already exists, you'll get the Connected with client!
// message before running the client. Delete this file first.
unsigned int x = 0;
while (true) {
valread = read(new_socket, recv_buffer, 4);
x = fromBytes(recv_buffer);
toBytes(x+1, send_buffer);
write(new_socket, send_buffer, 4);
}
}
C++客户端:
// tcpClientIncUnix.cpp
// Server side C/C++ program to demonstrate Socket programming
// #include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <unistd.h>
// Big Endian (network order)
unsigned int fromBytes(unsigned char b[4]) {
return b[3] | b[2]<<8 | b[1]<<16 | b[0]<<24;
}
void toBytes(unsigned int x, unsigned char (&b)[4]) {
b[3] = x;
b[2] = x>>8;
b[1] = x>>16;
b[0] = x>>24;
}
int main(int argc, char const *argv[])
{
int sock, valread;
struct sockaddr_un saddr;
int opt = 1;
int saddrlen = sizeof(saddr);
// We'll be passing uint32's back and forth
unsigned char recv_buffer[4] = {0};
unsigned char send_buffer[4] = {0};
sock = socket(AF_UNIX, SOCK_STREAM, 0);
saddr.sun_family = AF_UNIX;
strncpy(saddr.sun_path, "/tmp/cpp.sock", sizeof(saddr.sun_path));
saddr.sun_path[sizeof(saddr.sun_path)-1] = '\0';
// Accept one client connection
if (connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) != 0) {
throw("connect failed");
}
int n = 1000000;
unsigned int currNumber = 0;
while (currNumber < n) {
toBytes(currNumber, send_buffer);
write(sock, send_buffer, 4);
// Read the incremented number from server
valread = read(sock, recv_buffer, 4);
currNumber = fromBytes(recv_buffer);
}
}
go version devel +90dca98d33 Thu Dec 20 22:11:45 2018 +0000 linux/amd64
time ./client
real 0m7.653s user 0m1.803s sys 0m5.150s
- peterSOgo version go1.10.4 linux/amd64; time go client real 0m7.242s
,clang version 3.8.0-2ubuntu4; time c++ client real 0m4.942s
... 没有那么大的差异。 - tinkg++ (Ubuntu 8.2.0-7ubuntu1) 8.2.0
time ./a.out real 0m6.013s user 0m0.253s sys 0m4.564s
- peterSO