BenchmarkClient.swift 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. /*
  2. * Copyright 2024, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Foundation
  17. import GRPCCore
  18. import NIOConcurrencyHelpers
  19. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  20. struct BenchmarkClient {
  21. private var client: GRPCClient
  22. private var rpcNumber: Int32
  23. private var rpcType: Grpc_Testing_RpcType
  24. private let rpcStats: NIOLockedValueBox<RPCStats>
  25. init(
  26. client: GRPCClient,
  27. rpcNumber: Int32,
  28. rpcType: Grpc_Testing_RpcType,
  29. histogramParams: Grpc_Testing_HistogramParams?
  30. ) {
  31. self.client = client
  32. self.rpcNumber = rpcNumber
  33. self.rpcType = rpcType
  34. let histogram: RPCStats.LatencyHistogram
  35. if let histogramParams = histogramParams {
  36. histogram = .init(
  37. resolution: histogramParams.resolution,
  38. maxBucketStart: histogramParams.maxPossible
  39. )
  40. } else {
  41. histogram = .init()
  42. }
  43. self.rpcStats = NIOLockedValueBox(RPCStats(latencyHistogram: histogram))
  44. }
  45. internal var currentStats: RPCStats {
  46. return self.rpcStats.withLockedValue { stats in
  47. return stats
  48. }
  49. }
  50. internal func run() async throws {
  51. let benchmarkClient = Grpc_Testing_BenchmarkServiceClient(client: client)
  52. return try await withThrowingTaskGroup(of: Void.self) { clientGroup in
  53. // Start the client.
  54. clientGroup.addTask { try await client.run() }
  55. // Make the requests to the server and register the latency for each one.
  56. try await withThrowingTaskGroup(of: Void.self) { rpcsGroup in
  57. for _ in 0 ..< self.rpcNumber {
  58. rpcsGroup.addTask {
  59. let (latency, errorCode) = self.makeRPC(client: benchmarkClient, rpcType: self.rpcType)
  60. self.rpcStats.withLockedValue {
  61. $0.latencyHistogram.record(latency)
  62. if let errorCode = errorCode {
  63. $0.requestResultCount[errorCode, default: 1] += 1
  64. }
  65. }
  66. }
  67. }
  68. try await rpcsGroup.waitForAll()
  69. }
  70. try await clientGroup.next()
  71. }
  72. }
  73. // The result is the number of nanoseconds for processing the RPC.
  74. private func makeRPC(
  75. client: Grpc_Testing_BenchmarkServiceClient,
  76. rpcType: Grpc_Testing_RpcType
  77. ) -> (latency: Double, errorCode: RPCError.Code?) {
  78. switch rpcType {
  79. case .unary, .streaming, .streamingFromClient, .streamingFromServer, .streamingBothWays,
  80. .UNRECOGNIZED:
  81. let startTime = DispatchTime.now().uptimeNanoseconds
  82. let endTime = DispatchTime.now().uptimeNanoseconds
  83. return (
  84. latency: Double(endTime - startTime), errorCode: RPCError.Code(.unimplemented)
  85. )
  86. }
  87. }
  88. internal func shutdown() {
  89. self.client.close()
  90. }
  91. }