diff --git a/src/Config/SettableStorageComparer.php b/src/Config/SettableStorageComparer.php
new file mode 100644
index 0000000..60075bb
--- /dev/null
+++ b/src/Config/SettableStorageComparer.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Drupal\config_sync\Config;
+
+use Drupal\Core\Cache\MemoryBackend;
+use Drupal\Core\Config\CachedStorage;
+use Drupal\Core\Config\StorageComparer;
+use Drupal\Core\Config\StorageInterface;
+
+/**
+ * Defines a settable config storage comparer.
+ */
+class SettableStorageComparer extends StorageComparer {
+
+  /**
+   * Sets the source storage used to discover configuration changes.
+   *
+   * @param \Drupal\Core\Config\StorageInterface $storage
+   *
+   * @return $this
+   */
+  public function setSourceStorage(StorageInterface $storage) {
+    // Reset the static configuration data cache.
+    $this->sourceCacheStorage->deleteAll();
+    $this->sourceNames = [];
+
+    $this->sourceStorage = new CachedStorage(
+      $storage,
+      $this->sourceCacheStorage
+    );
+
+    $this->changelist = [StorageInterface::DEFAULT_COLLECTION => $this->getEmptyChangelist()];
+
+    return $this;
+  }
+
+  /**
+   * Sets the target storage used to discover configuration changes.
+   *
+   * @param \Drupal\Core\Config\StorageInterface $storage
+   *
+   * @return $this
+   */
+  public function setTargetStorage(StorageInterface $storage) {
+    // Reset the static configuration data cache.
+    $this->targetCacheStorage->deleteAll();
+    $this->targetNames = [];
+
+    $this->targetStorage = new CachedStorage(
+      $storage,
+      $this->targetCacheStorage
+    );
+
+    $this->changelist = [StorageInterface::DEFAULT_COLLECTION => $this->getEmptyChangelist()];
+
+    return $this;
+  }
+
+}
diff --git a/src/ConfigSyncLister.php b/src/ConfigSyncLister.php
index 0558f62..c42520f 100644
--- a/src/ConfigSyncLister.php
+++ b/src/ConfigSyncLister.php
@@ -2,13 +2,17 @@
 
 namespace Drupal\config_sync;
 
+use Drupal\config_normalizer\Config\NormalizedReadOnlyStorage;
+use Drupal\config_normalizer\Config\NormalizedReadOnlyStorageInterface;
 use Drupal\config_normalizer\Config\NormalizedStorageComparerTrait;
 use Drupal\config_normalizer\Plugin\ConfigNormalizerManager;
 use Drupal\config_snapshot\ConfigSnapshotStorageTrait;
+use Drupal\config_sync\Config\SettableStorageComparer;
 use Drupal\config_sync\Plugin\SyncConfigCollectorInterface;
 use Drupal\config_update\ConfigListInterface as ConfigUpdateListerInterface;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Config\ConfigManagerInterface;
+use Drupal\Core\Config\NullStorage;
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\Extension;
@@ -48,6 +52,13 @@ class ConfigSyncLister implements ConfigSyncListerInterface {
   protected $providerStorage;
 
   /**
+   * The normalized active storage.
+   *
+   * @var \Drupal\config_normalizer\Config\NormalizedReadOnlyStorageInterface
+   */
+  protected $normalizedActiveStorage;
+
+  /**
    * The state storage object.
    *
    * @var \Drupal\Core\State\StateInterface
@@ -64,6 +75,13 @@ class ConfigSyncLister implements ConfigSyncListerInterface {
   protected $configTypes = [];
 
   /**
+   * A storage comparer with the active storage as target.
+   *
+   * @var \Drupal\Core\Config\StorageComparerInterface
+   */
+  protected $activeStorageComparer;
+
+  /**
    * Constructs a ConfigSyncLister object.
    *
    * @param \Drupal\config_sync\Plugin\SyncConfigCollectorInterface $config_collector
@@ -90,6 +108,17 @@ class ConfigSyncLister implements ConfigSyncListerInterface {
     $this->providerStorage = $provider_storage;
     $this->setConfigManager($config_manager);
     $this->state = $state;
+    $this->normalizedActiveStorage = new NormalizedReadOnlyStorage($active_storage, $normalizer_manager);
+    // Set up a storage comparer to be used by each extension. Use a null
+    // storage as a placeholder that we'll reset. Using a single storage
+    // comparer rather than one per extension provides important optimization
+    // since each storage comparer will load all records into a memory cache
+    // and by setting a single source we can limit this to a single read.
+    $this->activeStorageComparer = new SettableStorageComparer(
+      new NormalizedReadOnlyStorage(new NullStorage(), $normalizer_manager),
+      $this->normalizedActiveStorage,
+      $config_manager
+    );
   }
 
   /**
@@ -128,8 +157,26 @@ class ConfigSyncLister implements ConfigSyncListerInterface {
 
     // For a full reset, compare against the active storage.
     if ($update_mode === ConfigSyncListerInterface::UPDATE_MODE_FULL_RESET) {
-      $active_storage = $this->getActiveStorages();
-      $storage_comparer = $this->createStorageComparer($this->providerStorage, $active_storage);
+      // Wrap the provider storage.
+      $normalized_provider_storage = new NormalizedReadOnlyStorage(
+        $this->providerStorage,
+        $this->normalizerManager,
+        [
+          'normalization_mode' => NormalizedReadOnlyStorageInterface::DEFAULT_NORMALIZATION_MODE,
+          'reference_storage_service' => $this->getActiveStorages(),
+        ]
+      );
+
+      // Set the provider storage as the comparer's source.
+      $this->activeStorageComparer->setSourceStorage($normalized_provider_storage);
+
+      // Set the context for the active storage.
+      $this->normalizedActiveStorage->setContext([
+        'normalization_mode' => NormalizedReadOnlyStorageInterface::DEFAULT_NORMALIZATION_MODE,
+        'reference_storage_service' => $this->providerStorage,
+      ]);
+
+      $storage_comparer = $this->activeStorageComparer;
     }
     // Otherwise, compare against a snapshot.
     else {
