benchmark_test.go 5.5 KB

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