Explorar o código

Pre-cache the services by name dictionary. (#931)

Motivation:

Currently whenever we construct the GRPCServerRequestRoutingHandler we
need to give it a dictionary of services by name. This is currently done
via a computed property. The result of that is that we compute this
dictionary on the fly every time we receive a new RPC, which is not
exactly the most efficient usage model.

Instead, we should just calculate this ahead of time and save the thing
we actually want, which is the Dictionary. As this data structure is
read vastly more often than it is written, this ends up being a
minor performance win in basically all server applications.

A note: technically this is _very_ subtly breaking as it no longer
guarantees that the Array containing the Services will be in the same
order when you read it as when you set it. I think this is acceptable:
the odds that anyone will rely on this behaviour is low. If we'd minted
1.0.0 I wouldn't take the risk, but right now I think it's ok to take it
and just document our way out of it.

Modifications:

- Replaced stored property storing services with one storing services by
  name.

Results:

Minor performance improvement.
Cory Benfield %!s(int64=5) %!d(string=hai) anos
pai
achega
36e80859db
Modificáronse 1 ficheiros con 15 adicións e 8 borrados
  1. 15 8
      Sources/GRPC/Server.swift

+ 15 - 8
Sources/GRPC/Server.swift

@@ -202,7 +202,14 @@ extension Server {
     public var eventLoopGroup: EventLoopGroup
 
     /// Providers the server should use to handle gRPC requests.
-    public var serviceProviders: [CallHandlerProvider]
+    public var serviceProviders: [CallHandlerProvider] {
+        get {
+            return Array(self.serviceProvidersByName.values)
+        }
+        set {
+            self.serviceProvidersByName = Dictionary(uniqueKeysWithValues: newValue.map { ($0.serviceName, $0) })
+        }
+    }
 
     /// An error delegate which is called when errors are caught. Provided delegates **must not
     /// maintain a strong reference to this `Server`**. Doing so will cause a retain cycle.
@@ -244,6 +251,12 @@ extension Server {
     ///   be invoked at most once per accepted connection.
     public var debugChannelInitializer: ((Channel) -> EventLoopFuture<Void>)?
 
+    /// A calculated private cache of the service providers by name.
+    ///
+    /// This is how gRPC consumes the service providers internally. Caching this as stored data avoids
+    /// the need to recalculate this dictionary each time we receive an rpc.
+    fileprivate private(set) var serviceProvidersByName: [Substring: CallHandlerProvider]
+
     /// Create a `Configuration` with some pre-defined defaults.
     ///
     /// - Parameters:
@@ -275,7 +288,7 @@ extension Server {
     ) {
       self.target = target
       self.eventLoopGroup = eventLoopGroup
-      self.serviceProviders = serviceProviders
+      self.serviceProvidersByName = Dictionary(uniqueKeysWithValues: serviceProviders.map { ($0.serviceName, $0) })
       self.errorDelegate = errorDelegate
       self.tls = tls
       self.connectionKeepalive = connectionKeepalive
@@ -288,12 +301,6 @@ extension Server {
   }
 }
 
-fileprivate extension Server.Configuration {
-  var serviceProvidersByName: [Substring: CallHandlerProvider] {
-    return Dictionary(uniqueKeysWithValues: self.serviceProviders.map { ($0.serviceName, $0) })
-  }
-}
-
 fileprivate extension Channel {
   /// Configure an SSL handler on the channel.
   ///