Scala vs. Go 的TCP基准测试

13-08-07 banq
         

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))
}
<p>

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)
    }
}
<p>

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

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

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

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

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