Scala vs. Go 的TCP基准测试

Scala vs. Go TCP Benchmark | 42 Engineering这篇文章对Scala和Go语言在Socket的TCP网络方面进行了一个比较测试。

GO语言代码:


//服务器端
package main

import (
"net"
"runtime"
)

func handleClient(conn net.Conn) {
defer conn.Close()

var buf [4]byte
for {
n, err := conn.Read(buf[0:])
if err!=nil {return}
if n>0 {
_, err = conn.Write([]byte(
"Pong"))
if err!=nil {return}
}
}
}

func main() {
runtime.GOMAXPROCS(4)

tcpAddr, _ := net.ResolveTCPAddr(
"tcp4", ":1201")
listener, _ := net.ListenTCP(
"tcp", tcpAddr)

for {
conn, _ := listener.Accept()
go handleClient(conn)
}
}


//客户端端代码
//CLIENT
package main

import (
"net"
"fmt"
"time"
"runtime"
)

func ping(times int, lockChan chan bool) {
tcpAddr, _ := net.ResolveTCPAddr(
"tcp4", "localhost:1201")
conn, _ := net.DialTCP(
"tcp", nil, tcpAddr)

for i:=0; i<int(times); i++ {
_, _ = conn.Write([]byte(
"Ping"))
var buff [4]byte
_, _ = conn.Read(buff[0:])
}
lockChan<-true
conn.Close()
}

func main() {
runtime.GOMAXPROCS(4)

var totalPings int = 1000000
var concurrentConnections int = 100
var pingsPerConnection int = totalPings/concurrentConnections
var actualTotalPings int = pingsPerConnection*concurrentConnections

lockChan := make(chan bool, concurrentConnections)

start := time.Now()
for i:=0; i<concurrentConnections; i++{
go ping(pingsPerConnection, lockChan)
}
for i:=0; i<int(concurrentConnections); i++{
<-lockChan
}
elapsed := 1000000*time.Since(start).Seconds()
fmt.Println(elapsed/float64(actualTotalPings))
}

Scala的测试代码:


//SERVER
import java.net._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._

object main{

def handleClient(s: Socket) : Unit = {
val in = s.getInputStream
val out = s.getOutputStream
while(s.isConnected){
val buffer = Array[Byte](4)
in.read(buffer)
out.write(
"Pong".getBytes)
}
}

def main(args: Array[String]){
val server = new ServerSocket(1201)
while(true){
val s: Socket = server.accept()
future { handleClient(s) }
}
}
}

//CLIENT
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import java.net._

object main{

def ping(timesToPing: Int) : Unit = {
val socket = new Socket(
"localhost", 1201)
val out = socket.getOutputStream
val in = socket.getInputStream
for (i <- 0 until timesToPing) {
out.write(
"Ping".getBytes)
val buffer = Array[Byte](4)
in.read(buffer)
}
socket.close
}

def main(args: Array[String]){
var totalPings = 1000000
var concurrentConnections = 100
var pingsPerConnection : Int = totalPings/concurrentConnections
var actualTotalPings : Int = pingsPerConnection*concurrentConnections

val t0 = (System.currentTimeMillis()).toDouble
var futures = (0 until concurrentConnections).map{_ =>
future(ping(pingsPerConnection))
}

Await.result(Future.sequence(futures), 1 minutes)
val t1 = (System.currentTimeMillis()).toDouble
println(1000*(t1-t0)/actualTotalPings)
}
}

测试结果令人惊讶:Scala竟然好于Go语言:

Scala的平均往返时间1.6 microseconds (0.0016 milliseconds) 而 Go语言是 ~11 microseconds (0.011 milliseconds).

文章的意思:GO语言固然很快,但是如果你的软件是类似传递TCP包,那么在吞吐量上面这是就有区别出来了,作者言下之意好像是性能快是一个方面,吞吐量大也很重要,所谓吞吐量:是否能够一口吃个大胖子,还是一口只能吃个小鸡块。

Scala也为吞吐量付出代价,内存占用200MB,而GO只有10MB。


作者最后总结:GO语言还很新,有很多性能提升的地方,它的稳定且简单的并发原始模型可以弥补使上述一些缺陷。