diff --git a/src/SimpleInjector/Internals/GenericRegistrationEntry.cs b/src/SimpleInjector/Internals/GenericRegistrationEntry.cs
index 7fc84af1..f13a083e 100644
--- a/src/SimpleInjector/Internals/GenericRegistrationEntry.cs
+++ b/src/SimpleInjector/Internals/GenericRegistrationEntry.cs
@@ -12,6 +12,12 @@ internal sealed class GenericRegistrationEntry : IRegistrationEntry
         private readonly List<IProducerProvider> providers = new();
         private readonly ContainerOptions options;
 
+        // PERF: #985 These two collections exist solely for performance optimizations. Registering many
+        // closed-generic types of the same generic abstractions got exponentially slower with the number
+        // of registrations. These two collections help optimize this.
+        private Dictionary<Type, ClosedToInstanceProducerProviderDictionaryEntry>? closedProviders;
+        private List<OpenGenericToInstanceProducerProvider>? openProviders;
+
         internal GenericRegistrationEntry(Container container)
         {
             this.options = container.Options;
@@ -49,19 +55,55 @@ public void Add(InstanceProducer producer)
         {
             this.Container.ThrowWhenContainerIsLockedOrDisposed();
 
-            this.ThrowWhenConditionalIsRegisteredInOverridingMode(producer);
+            Type serviceType = producer.ServiceType;
 
             if (!this.AllowOverridingRegistrations)
             {
-                this.ThrowWhenOverlappingRegistrationsExist(producer); // O(n) operation
+                this.ThrowWhenOverlappingRegistrationsExist(producer);
             }
-
-            if (this.AllowOverridingRegistrations)
+            else
             {
-                this.providers.RemoveAll(p => p.ServiceType == producer.ServiceType); // O(n) operation
+                this.ThrowWhenConditionalIsRegisteredInOverridingMode(producer);
+
+                if (this.closedProviders != null)
+                {
+                    if (this.closedProviders.ContainsKey(serviceType))
+                    {
+                        this.closedProviders.Remove(serviceType);
+                        this.providers.RemoveAll(p => p.ServiceType == serviceType);
+                    }
+                }
             }
 
-            this.providers.Add(new ClosedToInstanceProducerProvider(producer));
+            var provider = new ClosedToInstanceProducerProvider(producer);
+            
+            this.AddClosedToInstanceProducerProvider(provider);
+        }
+
+        private void AddClosedToInstanceProducerProvider(ClosedToInstanceProducerProvider provider)
+        {
+            Type serviceType = provider.ServiceType;
+
+            this.providers.Add(provider);
+
+            if (this.closedProviders is null)
+            {
+                this.closedProviders = new Dictionary<Type, ClosedToInstanceProducerProviderDictionaryEntry>
+                {
+                    [serviceType] = new ClosedToInstanceProducerProviderDictionaryEntry(provider)
+                };
+            }
+            else
+            {
+                if (this.closedProviders.TryGetValue(serviceType, out var entry))
+                {
+                    entry.Add(provider);
+                }
+                else
+                {
+                    this.closedProviders[serviceType] = new ClosedToInstanceProducerProviderDictionaryEntry(provider);
+                }
+            }
         }
 
         public void AddGeneric(
@@ -84,10 +126,12 @@ public void AddGeneric(
                 if (provider.GetAppliesToAllClosedServiceTypes())
                 {
                     this.providers.RemoveAll(p => p.GetAppliesToAllClosedServiceTypes());
+
+                    this.openProviders?.RemoveAll(p => p.GetAppliesToAllClosedServiceTypes());
                 }
             }
 
-            this.providers.Add(provider);
+            this.AddOpenGenericToInstanceProducerProvider(provider);
         }
 
         public void Add(
@@ -105,7 +149,19 @@ public void Add(
 
             this.ThrowWhenProviderToRegisterOverlapsWithExistingProvider(provider);
 
+            this.AddOpenGenericToInstanceProducerProvider(provider);
+        }
+
+        private void AddOpenGenericToInstanceProducerProvider(OpenGenericToInstanceProducerProvider provider)
+        {
             this.providers.Add(provider);
+
+            if (this.openProviders is null)
+            {
+                this.openProviders = new List<OpenGenericToInstanceProducerProvider>();
+            }
+
+            this.openProviders.Add(provider);
         }
 
         public InstanceProducer? TryGetInstanceProducer(
@@ -172,14 +228,49 @@ private void ThrowWhenOverlappingRegistrationsExist(InstanceProducer producerToR
 
         private IProducerProvider? GetFirstOverlappingProvider(InstanceProducer producerToRegister)
         {
+            ClosedToInstanceProducerProvider? firstOverlappingClosedProvider = null;
+            OpenGenericToInstanceProducerProvider? firstOverlappingOpenProvider = null;
+
+            if (this.closedProviders != null)
+            {
+                // PERF: Only closed providers exist. We can speed up the operation by going just through
+                // the closed providers for the given service type.
+                if (this.closedProviders.TryGetValue(producerToRegister.ServiceType, out var entry))
+                {
+                    firstOverlappingClosedProvider = entry.OverlapsWith(producerToRegister);
+                }
+            }
+
+            if (this.openProviders != null)
+            {
+                foreach (var openProvider in this.openProviders)
+                {
+                    if (openProvider.OverlapsWith(producerToRegister))
+                    {
+                        firstOverlappingOpenProvider = openProvider;
+                        break;
+                    }
+                }
+            }
+
+            // PERF: Is most cases we can prevent going through the list when there's only one of the two
+            // overlapping.
+            if (firstOverlappingClosedProvider is null && firstOverlappingOpenProvider is null) return null;
+            if (firstOverlappingClosedProvider != null) return firstOverlappingClosedProvider;
+            if (firstOverlappingOpenProvider != null) return firstOverlappingOpenProvider;
+
+            // To bad, there is both an overlapping open and a closed provider. Since we must report the first
+            // first overlapping provider, we have to go through the list (again).
             foreach (var provider in this.providers)
             {
-                if (provider.OverlapsWith(producerToRegister))
+                if (provider == firstOverlappingClosedProvider ||
+                    provider == firstOverlappingOpenProvider)
                 {
                     return provider;
                 }
             }
 
+            // Will never come here, but the C# compiler doesn't know.
             return null;
         }
 
@@ -541,5 +632,55 @@ private bool IsImplementationApplicableToEveryGenericType(Type implementationTyp
                     this.ServiceType,
                     implementationType);
         }
+
+        private sealed class ClosedToInstanceProducerProviderDictionaryEntry
+        {
+            private readonly ClosedToInstanceProducerProvider firstProvider;
+
+            private List<ClosedToInstanceProducerProvider>? providers;
+
+            public ClosedToInstanceProducerProviderDictionaryEntry(ClosedToInstanceProducerProvider firstProvider)
+            {
+                this.firstProvider = firstProvider;
+            }
+
+            public int Count => this.providers?.Count ?? 1;
+
+            internal void Add(ClosedToInstanceProducerProvider provider)
+            {
+                if (this.providers is null)
+                {
+                    this.providers = new List<ClosedToInstanceProducerProvider>
+                    {
+                        this.firstProvider,
+                        provider
+                    };
+                }
+                else
+                {
+                    this.providers.Add(provider);
+                }
+            }
+
+            internal ClosedToInstanceProducerProvider? OverlapsWith(InstanceProducer producerToRegister)
+            {
+                if (this.providers is null)
+                {
+                    return this.firstProvider.OverlapsWith(producerToRegister) ? this.firstProvider : null;
+                }
+                else
+                {
+                    foreach (var provider in this.providers)
+                    {
+                        if (provider.OverlapsWith(producerToRegister))
+                        {
+                            return provider;
+                        }
+                    }
+
+                    return null;
+                }
+            }
+        }
     }
 }
\ No newline at end of file