Skip to content

Instantly share code, notes, and snippets.

@jochumdev
Last active March 16, 2017 19:10
Show Gist options
  • Save jochumdev/6168792 to your computer and use it in GitHub Desktop.
Save jochumdev/6168792 to your computer and use it in GitHub Desktop.
Go RPC Benchmarks, encoding/gob seems to be the fastest RPC encoder for Go.
// Copyright 2013 René Kistl. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package srpc
import (
"errors"
"fmt"
"github.com/pcdummy/skynet2/rpc/bsonrpc"
"github.com/ugorji/go/codec"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"runtime"
"sync"
"sync/atomic"
"testing"
)
var (
serverBSON, serverGob, serverMsgPack, serverBinc, serverJSON string
)
type Args struct {
A, B int
}
type Reply struct {
C int
}
type Arith int
// Some of Arith's methods have value args, some have pointer args. That's deliberate.
func (t *Arith) Add(args Args, reply *Reply) error {
reply.C = args.A + args.B
return nil
}
func (t *Arith) Mul(args *Args, reply *Reply) error {
reply.C = args.A * args.B
return nil
}
func (t *Arith) Div(args Args, reply *Reply) error {
if args.B == 0 {
return errors.New("divide by zero")
}
reply.C = args.A / args.B
return nil
}
func (t *Arith) String(args *Args, reply *string) error {
*reply = fmt.Sprintf("%d+%d=%d", args.A, args.B, args.A+args.B)
return nil
}
func (t *Arith) Scan(args string, reply *Reply) (err error) {
_, err = fmt.Sscan(args, &reply.C)
return
}
func (t *Arith) Error(args *Args, reply *Reply) error {
panic("ERROR")
}
func listenTCP() (net.Listener, string) {
l, e := net.Listen("tcp", "127.0.0.1:0") // any available address
if e != nil {
log.Fatalf("net.Listen tcp :0: %v", e)
}
return l, l.Addr().String()
}
func TestStartServers(t *testing.T) {
startBSONServer()
startMsgPackServer()
startGobServer()
startBincServer()
startJSONServer()
}
func startBSONServer() {
rpc.Register(new(Arith))
var l net.Listener
l, serverBSON = listenTCP()
// log.Println("Test BSON RPC server listening on", serverAddr)
go func() {
for {
conn, err := l.Accept()
if err != nil {
log.Fatal("accept error:", err)
}
codec := bsonrpc.NewServerCodec(conn)
go rpc.ServeCodec(codec)
}
}()
}
func startMsgPackServer() {
var l net.Listener
l, serverMsgPack = listenTCP()
// log.Println("Test MsgPack RPC server listening on", serverAddr2)
go func() {
for {
conn, err := l.Accept()
if err != nil {
log.Fatal("accept error:", err)
}
codec := codec.GoRpc.ServerCodec(conn, &codec.MsgpackHandle{})
go rpc.ServeCodec(codec)
}
}()
}
func startGobServer() {
var l net.Listener
l, serverGob = listenTCP()
// log.Println("Test MsgPack RPC server listening on", serverAddr2)
go func() {
for {
conn, err := l.Accept()
if err != nil {
log.Fatal("accept error:", err)
}
go rpc.ServeConn(conn)
}
}()
}
func startBincServer() {
var l net.Listener
l, serverBinc = listenTCP()
// log.Println("Test MsgPack RPC server listening on", serverAddr2)
go func() {
for {
conn, err := l.Accept()
if err != nil {
log.Fatal("accept error:", err)
}
codec := codec.GoRpc.ServerCodec(conn, &codec.BincHandle{})
go rpc.ServeCodec(codec)
}
}()
}
func startJSONServer() {
var l net.Listener
l, serverJSON = listenTCP()
// log.Println("Test MsgPack RPC server listening on", serverAddr2)
go func() {
for {
conn, err := l.Accept()
if err != nil {
log.Fatal("accept error:", err)
}
codec := jsonrpc.NewServerCodec(conn)
go rpc.ServeCodec(codec)
}
}()
}
func benchmarkClient(client *rpc.Client, b *testing.B) {
// Synchronous calls
args := &Args{7, 8}
procs := runtime.GOMAXPROCS(-1)
N := int32(b.N)
var wg sync.WaitGroup
wg.Add(procs)
b.StartTimer()
for p := 0; p < procs; p++ {
go func() {
reply := new(Reply)
for atomic.AddInt32(&N, -1) >= 0 {
err := client.Call("Arith.Add", args, reply)
if err != nil {
b.Fatalf("rpc error: Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
b.Fatalf("rpc error: Add: expected %d got %d", reply.C, args.A+args.B)
}
}
wg.Done()
}()
}
wg.Wait()
}
func BenchmarkEndToEndBSON(b *testing.B) {
b.StopTimer()
conn, err := net.Dial("tcp", serverBSON)
if err != nil {
log.Fatal("error dialing:", err)
}
client := bsonrpc.NewClient(conn)
defer client.Close()
benchmarkClient(client, b)
}
func BenchmarkEndToEndMsgPack(b *testing.B) {
b.StopTimer()
conn, err := net.Dial("tcp", serverMsgPack)
if err != nil {
log.Fatal("error dialing:", err)
}
rpcCodec := codec.GoRpc.ClientCodec(conn, &codec.MsgpackHandle{})
client := rpc.NewClientWithCodec(rpcCodec)
defer client.Close()
benchmarkClient(client, b)
}
func BenchmarkEndToEndGob(b *testing.B) {
b.StopTimer()
conn, err := net.Dial("tcp", serverGob)
if err != nil {
log.Fatal("error dialing:", err)
}
client := rpc.NewClient(conn)
defer client.Close()
benchmarkClient(client, b)
}
func BenchmarkEndToEndBinc(b *testing.B) {
b.StopTimer()
conn, err := net.Dial("tcp", serverBinc)
if err != nil {
log.Fatal("error dialing:", err)
}
rpcCodec := codec.GoRpc.ClientCodec(conn, &codec.BincHandle{})
client := rpc.NewClientWithCodec(rpcCodec)
defer client.Close()
benchmarkClient(client, b)
}
func BenchmarkEndToEndJSON(b *testing.B) {
b.StopTimer()
conn, err := net.Dial("tcp", serverJSON)
if err != nil {
log.Fatal("error dialing:", err)
}
rpcCodec := jsonrpc.NewClientCodec(conn)
client := rpc.NewClientWithCodec(rpcCodec)
defer client.Close()
benchmarkClient(client, b)
}
@jochumdev
Copy link
Author

pcdummy@ThinkPad-T410 ~/Projekte/golang/src/rpc_test $ go test -test.bench=".*" bson_bench_test.go
PASS
BenchmarkEndToEndBSON 20000 79669 ns/op
BenchmarkEndToEndMsgPack 10000 105534 ns/op
BenchmarkEndToEndGob 50000 45514 ns/op
BenchmarkEndToEndBinc 10000 103352 ns/op
BenchmarkEndToEndJSON 50000 71928 ns/op
ok command-line-arguments 11.593s

My Results on a Thinkpad T410

@ugorji
Copy link

ugorji commented Aug 7, 2013

Please sync and try again. This should now be fixed. RPC had to perform input reader buffering, which is what gob and json do internally.

@jochumdev
Copy link
Author

Yes, ugorji adding a buffer fixes it.

pcdummy@ThinkPad-T410 ~/Projekte/golang/src/rpc_test $ go test -test.bench=".*" bson_bench_test.go
PASS
BenchmarkEndToEndBSON 50000 66116 ns/op
BenchmarkEndToEndMsgPack 50000 45132 ns/op
BenchmarkEndToEndGob 50000 39081 ns/op
BenchmarkEndToEndBinc 50000 45951 ns/op
BenchmarkEndToEndJSON 50000 59995 ns/op
ok command-line-arguments 15.419s

@jochumdev
Copy link
Author

New Test results, added bufio to BSON RPC.

pcdummy@ThinkPad-T410 ~/Projekte/golang/src/rpc_test $ go test -test.bench=".*" bson_bench_test.go
PASS
BenchmarkEndToEndBSON 50000 46393 ns/op
BenchmarkEndToEndMsgPack 50000 43229 ns/op
BenchmarkEndToEndGob 50000 39441 ns/op
BenchmarkEndToEndBinc 50000 45499 ns/op
BenchmarkEndToEndJSON 50000 61448 ns/op
ok command-line-arguments 14.225s

@jochumdev
Copy link
Author

New results with a faster MsgPack, thx to ugorji

BenchmarkEndToEndBSON 50000 45805 ns/op
BenchmarkEndToEndMsgPack 50000 41245 ns/op
BenchmarkEndToEndGob 50000 39104 ns/op
BenchmarkEndToEndBinc 50000 42863 ns/op
BenchmarkEndToEndJSON 50000 61911 ns/op

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment