veyron/runtimes/google/ipc/stream/crypto: Add benchmarks for connection
setup.
This commit only adds the benchmarks and make a strong case for using nacl/box
as the encryption layer since by avoiding unnecessary certification
verification, the handshake time improves by 140x on my desktop and 10x on the
raspberry pi.
The benchmarks demonstrate (and mimic real-world usage) that connection setup
to a Pi takes 3 seconds with the current code and can be improved to 200ms
using the box crypter.
I also tried the TLS benchmarks using an ECDSA certificate and the
ECDHE_ECDSA_WITH_RC4_128_SHA cipher suite instead of an RSA certificate
with TLS_ECDHE_RSA_WITH_RC4_128_SHA. Switching to ECDSA certificates
also improves time considerably, but really we need to figure out
a way to avoid certificate verification (especially since we do not
place any faith in these certificates). Either by switching to box
or by using TLS_DH_anon_* or some family of cipher suites in TLS.
Benchmark summary:
Desktop (Intel Xeon 3.2Ghz)
us-per-handshake speedup
TLS with RSA keys 32938 1x (baseline)
TLS with ECDSA keys 8143 4x
Box 223 147x
Raspberri Pi ms-per-handshake speedup
TLS with RSA keys 2856 1x (baseline)
TLS with ECDSA keys 739 3x
Box 290 10x
Change-Id: Icb60a3cc1be33ff63c3b2a0562fbf18feff38753
diff --git a/runtimes/google/ipc/stream/crypto/crypto_test.go b/runtimes/google/ipc/stream/crypto/crypto_test.go
index 8720ccc..84b0d76 100644
--- a/runtimes/google/ipc/stream/crypto/crypto_test.go
+++ b/runtimes/google/ipc/stream/crypto/crypto_test.go
@@ -37,8 +37,7 @@
crypter.String() // Only to test that String does not crash.
}
-func testSimple(t *testing.T, crypters func(testing.TB) (Crypter, Crypter)) {
- c1, c2 := crypters(t)
+func testSimple(t *testing.T, c1, c2 Crypter) {
// Execute String just to check that it does not crash.
c1.String()
c2.String()
@@ -63,11 +62,21 @@
t.Logf("Byte overhead of encryption: %v", overhead)
}
-func TestTLS(t *testing.T) { testSimple(t, tlsCrypters) }
-func TestBox(t *testing.T) { testSimple(t, boxCrypters) }
+func TestTLS(t *testing.T) {
+ server, client := net.Pipe()
+ c1, c2 := tlsCrypters(t, server, client)
+ testSimple(t, c1, c2)
+}
+
+func TestBox(t *testing.T) {
+ server, client := net.Pipe()
+ c1, c2 := boxCrypters(t, server, client)
+ testSimple(t, c1, c2)
+}
func TestTLSNil(t *testing.T) {
- c1, c2 := tlsCrypters(t)
+ conn1, conn2 := net.Pipe()
+ c1, c2 := tlsCrypters(t, conn1, conn2)
if t.Failed() {
return
}
@@ -89,7 +98,8 @@
// 16K (it is represented by a uint16).
// http://tools.ietf.org/html/rfc5246#section-6.2.1
const dataLen = 16384 + 1
- enc, dec := tlsCrypters(t)
+ conn1, conn2 := net.Pipe()
+ enc, dec := tlsCrypters(t, conn1, conn2)
cipher, err := enc.Encrypt(iobuf.NewSlice(make([]byte, dataLen)))
if err != nil {
t.Fatal(err)
@@ -103,8 +113,9 @@
}
}
-func tlsCrypters(t testing.TB) (Crypter, Crypter) {
- serverConn, clientConn := net.Pipe()
+type factory func(t testing.TB, server, client net.Conn) (Crypter, Crypter)
+
+func tlsCrypters(t testing.TB, serverConn, clientConn net.Conn) (Crypter, Crypter) {
crypters := make(chan Crypter)
go func() {
server, err := NewTLSServer(serverConn, iobuf.NewPool(0))
@@ -126,8 +137,7 @@
return c1, c2
}
-func boxCrypters(t testing.TB) (Crypter, Crypter) {
- serverConn, clientConn := net.Pipe()
+func boxCrypters(t testing.TB, serverConn, clientConn net.Conn) (Crypter, Crypter) {
crypters := make(chan Crypter)
for _, conn := range []net.Conn{serverConn, clientConn} {
go func(conn net.Conn) {
@@ -141,12 +151,15 @@
return <-crypters, <-crypters
}
-func benchmarkEncrypt(b *testing.B, crypters func(testing.TB) (Crypter, Crypter), size int) {
+func benchmarkEncrypt(b *testing.B, crypters factory, size int) {
plaintext := make([]byte, size)
if _, err := rand.Read(plaintext); err != nil {
b.Fatal(err)
}
- e, _ := crypters(b)
+ conn1, conn2 := net.Pipe()
+ defer conn1.Close()
+ defer conn2.Close()
+ e, _ := crypters(b, conn1, conn2)
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -170,12 +183,15 @@
func BenchmarkBoxEncrypt_1M(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 1<<20) }
func BenchmarkBoxEncrypt_5M(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 5<<20) }
-func benchmarkRoundTrip(b *testing.B, crypters func(testing.TB) (Crypter, Crypter), size int) {
+func benchmarkRoundTrip(b *testing.B, crypters factory, size int) {
plaintext := make([]byte, size)
if _, err := rand.Read(plaintext); err != nil {
b.Fatal(err)
}
- e, d := crypters(b)
+ conn1, conn2 := net.Pipe()
+ defer conn1.Close()
+ defer conn2.Close()
+ e, d := crypters(b, conn1, conn2)
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -201,3 +217,15 @@
func BenchmarkBoxRoundTrip_10K(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 10<<10) }
func BenchmarkBoxRoundTrip_1M(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 1<<20) }
func BenchmarkBoxRoundTrip_5M(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 5<<20) }
+
+func benchmarkSetup(b *testing.B, crypters factory) {
+ for i := 0; i < b.N; i++ {
+ conn1, conn2 := net.Pipe()
+ crypters(b, conn1, conn2)
+ conn1.Close()
+ conn2.Close()
+ }
+}
+
+func BenchmarkTLSSetup(b *testing.B) { benchmarkSetup(b, tlsCrypters) }
+func BenchmarkBoxSetup(b *testing.B) { benchmarkSetup(b, boxCrypters) }