benchmark_test.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Copyright (C) 2016 The Protocol Authors.
  2. package protocol
  3. import (
  4. "context"
  5. "crypto/tls"
  6. "encoding/binary"
  7. "net"
  8. "testing"
  9. "github.com/syncthing/syncthing/lib/dialer"
  10. "github.com/syncthing/syncthing/lib/testutil"
  11. )
  12. func BenchmarkRequestsRawTCP(b *testing.B) {
  13. // Benchmarks the rate at which we can serve requests over a single,
  14. // unencrypted TCP channel over the loopback interface.
  15. // Get a connected TCP pair
  16. conn0, conn1, err := getTCPConnectionPair()
  17. if err != nil {
  18. b.Fatal(err)
  19. }
  20. defer conn0.Close()
  21. defer conn1.Close()
  22. // Bench it
  23. benchmarkRequestsConnPair(b, conn0, conn1)
  24. }
  25. func BenchmarkRequestsTLSoTCP(b *testing.B) {
  26. conn0, conn1, err := getTCPConnectionPair()
  27. if err != nil {
  28. b.Fatal(err)
  29. }
  30. defer conn0.Close()
  31. defer conn1.Close()
  32. benchmarkRequestsTLS(b, conn0, conn1)
  33. }
  34. func benchmarkRequestsTLS(b *testing.B, conn0, conn1 net.Conn) {
  35. // Benchmarks the rate at which we can serve requests over a single,
  36. // TLS encrypted channel over the loopback interface.
  37. // Load a certificate, skipping this benchmark if it doesn't exist
  38. cert, err := tls.LoadX509KeyPair("../../test/h1/cert.pem", "../../test/h1/key.pem")
  39. if err != nil {
  40. b.Skip(err)
  41. return
  42. }
  43. /// TLSify them
  44. conn0, conn1 = negotiateTLS(cert, conn0, conn1)
  45. // Bench it
  46. benchmarkRequestsConnPair(b, conn0, conn1)
  47. }
  48. func benchmarkRequestsConnPair(b *testing.B, conn0, conn1 net.Conn) {
  49. // Start up Connections on them
  50. c0 := NewConnection(LocalDeviceID, conn0, conn0, testutil.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, nil, testKeyGen)
  51. c0.Start()
  52. c1 := NewConnection(LocalDeviceID, conn1, conn1, testutil.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, nil, testKeyGen)
  53. c1.Start()
  54. // Satisfy the assertions in the protocol by sending an initial cluster config
  55. c0.ClusterConfig(ClusterConfig{})
  56. c1.ClusterConfig(ClusterConfig{})
  57. // Report some useful stats and reset the timer for the actual test
  58. b.ReportAllocs()
  59. b.SetBytes(128 << 10)
  60. b.ResetTimer()
  61. // Request 128 KiB blocks, which will be satisfied by zero copy from the
  62. // other side (we'll get back a full block of zeroes).
  63. var buf []byte
  64. var err error
  65. for i := 0; i < b.N; i++ {
  66. // Use c0 and c1 for each alternating request, so we get as much
  67. // data flowing in both directions.
  68. if i%2 == 0 {
  69. buf, err = c0.Request(context.Background(), "folder", "file", i, int64(i), 128<<10, nil, 0, false)
  70. } else {
  71. buf, err = c1.Request(context.Background(), "folder", "file", i, int64(i), 128<<10, nil, 0, false)
  72. }
  73. if err != nil {
  74. b.Fatal(err)
  75. }
  76. if len(buf) != 128<<10 {
  77. b.Fatal("Incorrect returned buf length", len(buf), "!=", 128<<10)
  78. }
  79. // The fake model is supposed to tag the end of the buffer with the
  80. // requested offset, so we can verify that we get back data for this
  81. // block correctly.
  82. if binary.BigEndian.Uint64(buf[128<<10-8:]) != uint64(i) {
  83. b.Fatal("Bad data returned")
  84. }
  85. }
  86. }
  87. // returns the two endpoints of a TCP connection over lo0
  88. func getTCPConnectionPair() (net.Conn, net.Conn, error) {
  89. lst, err := net.Listen("tcp", "127.0.0.1:0")
  90. if err != nil {
  91. return nil, nil, err
  92. }
  93. // We run the Accept in the background since it's blocking, and we use
  94. // the channel to make the race thingies happy about writing vs reading
  95. // conn0 and err0.
  96. var conn0 net.Conn
  97. var err0 error
  98. done := make(chan struct{})
  99. go func() {
  100. conn0, err0 = lst.Accept()
  101. close(done)
  102. }()
  103. // Dial the connection
  104. conn1, err := net.Dial("tcp", lst.Addr().String())
  105. if err != nil {
  106. return nil, nil, err
  107. }
  108. // Check any error from accept
  109. <-done
  110. if err0 != nil {
  111. return nil, nil, err0
  112. }
  113. // Set the buffer sizes etc as usual
  114. dialer.SetTCPOptions(conn0)
  115. dialer.SetTCPOptions(conn1)
  116. return conn0, conn1, nil
  117. }
  118. func negotiateTLS(cert tls.Certificate, conn0, conn1 net.Conn) (net.Conn, net.Conn) {
  119. cfg := &tls.Config{
  120. Certificates: []tls.Certificate{cert},
  121. NextProtos: []string{"bep/1.0"},
  122. ClientAuth: tls.RequestClientCert,
  123. SessionTicketsDisabled: true,
  124. InsecureSkipVerify: true,
  125. MinVersion: tls.VersionTLS12,
  126. CipherSuites: []uint16{
  127. tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
  128. tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
  129. tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
  130. tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
  131. tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
  132. tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
  133. },
  134. }
  135. tlsc0 := tls.Server(conn0, cfg)
  136. tlsc1 := tls.Client(conn1, cfg)
  137. return tlsc0, tlsc1
  138. }
  139. // The fake model does nothing much
  140. type fakeModel struct{}
  141. func (*fakeModel) Index(Connection, string, []FileInfo) error {
  142. return nil
  143. }
  144. func (*fakeModel) IndexUpdate(Connection, string, []FileInfo) error {
  145. return nil
  146. }
  147. func (*fakeModel) Request(_ Connection, _, _ string, _, size int32, offset int64, _ []byte, _ uint32, _ bool) (RequestResponse, error) {
  148. // We write the offset to the end of the buffer, so the receiver
  149. // can verify that it did in fact get some data back over the
  150. // connection.
  151. buf := make([]byte, size)
  152. binary.BigEndian.PutUint64(buf[len(buf)-8:], uint64(offset))
  153. return &fakeRequestResponse{buf}, nil
  154. }
  155. func (*fakeModel) ClusterConfig(Connection, ClusterConfig) error {
  156. return nil
  157. }
  158. func (*fakeModel) Closed(Connection, error) {
  159. }
  160. func (*fakeModel) DownloadProgress(Connection, string, []FileDownloadProgressUpdate) error {
  161. return nil
  162. }