benchmark_test.go 6.2 KB

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