You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

142 lines
4.3 KiB

  1. /*
  2. *
  3. * Copyright 2017 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package transport
  19. import (
  20. "sync"
  21. "time"
  22. )
  23. const (
  24. // bdpLimit is the maximum value the flow control windows will be increased
  25. // to. TCP typically limits this to 4MB, but some systems go up to 16MB.
  26. // Since this is only a limit, it is safe to make it optimistic.
  27. bdpLimit = (1 << 20) * 16
  28. // alpha is a constant factor used to keep a moving average
  29. // of RTTs.
  30. alpha = 0.9
  31. // If the current bdp sample is greater than or equal to
  32. // our beta * our estimated bdp and the current bandwidth
  33. // sample is the maximum bandwidth observed so far, we
  34. // increase our bbp estimate by a factor of gamma.
  35. beta = 0.66
  36. // To put our bdp to be smaller than or equal to twice the real BDP,
  37. // we should multiply our current sample with 4/3, however to round things out
  38. // we use 2 as the multiplication factor.
  39. gamma = 2
  40. )
  41. // Adding arbitrary data to ping so that its ack can be identified.
  42. // Easter-egg: what does the ping message say?
  43. var bdpPing = &ping{data: [8]byte{2, 4, 16, 16, 9, 14, 7, 7}}
  44. type bdpEstimator struct {
  45. // sentAt is the time when the ping was sent.
  46. sentAt time.Time
  47. mu sync.Mutex
  48. // bdp is the current bdp estimate.
  49. bdp uint32
  50. // sample is the number of bytes received in one measurement cycle.
  51. sample uint32
  52. // bwMax is the maximum bandwidth noted so far (bytes/sec).
  53. bwMax float64
  54. // bool to keep track of the beginning of a new measurement cycle.
  55. isSent bool
  56. // Callback to update the window sizes.
  57. updateFlowControl func(n uint32)
  58. // sampleCount is the number of samples taken so far.
  59. sampleCount uint64
  60. // round trip time (seconds)
  61. rtt float64
  62. }
  63. // timesnap registers the time bdp ping was sent out so that
  64. // network rtt can be calculated when its ack is received.
  65. // It is called (by controller) when the bdpPing is
  66. // being written on the wire.
  67. func (b *bdpEstimator) timesnap(d [8]byte) {
  68. if bdpPing.data != d {
  69. return
  70. }
  71. b.sentAt = time.Now()
  72. }
  73. // add adds bytes to the current sample for calculating bdp.
  74. // It returns true only if a ping must be sent. This can be used
  75. // by the caller (handleData) to make decision about batching
  76. // a window update with it.
  77. func (b *bdpEstimator) add(n uint32) bool {
  78. b.mu.Lock()
  79. defer b.mu.Unlock()
  80. if b.bdp == bdpLimit {
  81. return false
  82. }
  83. if !b.isSent {
  84. b.isSent = true
  85. b.sample = n
  86. b.sentAt = time.Time{}
  87. b.sampleCount++
  88. return true
  89. }
  90. b.sample += n
  91. return false
  92. }
  93. // calculate is called when an ack for a bdp ping is received.
  94. // Here we calculate the current bdp and bandwidth sample and
  95. // decide if the flow control windows should go up.
  96. func (b *bdpEstimator) calculate(d [8]byte) {
  97. // Check if the ping acked for was the bdp ping.
  98. if bdpPing.data != d {
  99. return
  100. }
  101. b.mu.Lock()
  102. rttSample := time.Since(b.sentAt).Seconds()
  103. if b.sampleCount < 10 {
  104. // Bootstrap rtt with an average of first 10 rtt samples.
  105. b.rtt += (rttSample - b.rtt) / float64(b.sampleCount)
  106. } else {
  107. // Heed to the recent past more.
  108. b.rtt += (rttSample - b.rtt) * float64(alpha)
  109. }
  110. b.isSent = false
  111. // The number of bytes accumulated so far in the sample is smaller
  112. // than or equal to 1.5 times the real BDP on a saturated connection.
  113. bwCurrent := float64(b.sample) / (b.rtt * float64(1.5))
  114. if bwCurrent > b.bwMax {
  115. b.bwMax = bwCurrent
  116. }
  117. // If the current sample (which is smaller than or equal to the 1.5 times the real BDP) is
  118. // greater than or equal to 2/3rd our perceived bdp AND this is the maximum bandwidth seen so far, we
  119. // should update our perception of the network BDP.
  120. if float64(b.sample) >= beta*float64(b.bdp) && bwCurrent == b.bwMax && b.bdp != bdpLimit {
  121. sampleFloat := float64(b.sample)
  122. b.bdp = uint32(gamma * sampleFloat)
  123. if b.bdp > bdpLimit {
  124. b.bdp = bdpLimit
  125. }
  126. bdp := b.bdp
  127. b.mu.Unlock()
  128. b.updateFlowControl(bdp)
  129. return
  130. }
  131. b.mu.Unlock()
  132. }