diff --git a/core/core.services.yml b/core/core.services.yml
index 9a36139..3e0a206 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -58,6 +58,8 @@ services:
   config.storage:
     class: Drupal\Core\Config\CachedStorage
     arguments: ['@config.cachedstorage.storage', '@cache.config']
+    tags:
+      - { name: persist }
   config.context.factory:
     class: Drupal\Core\Config\Context\ConfigContextFactory
     arguments: ['@event_dispatcher']
diff --git a/core/lib/Drupal/Core/Config/CachedStorage.php b/core/lib/Drupal/Core/Config/CachedStorage.php
index 7d094b4..f98542f 100644
--- a/core/lib/Drupal/Core/Config/CachedStorage.php
+++ b/core/lib/Drupal/Core/Config/CachedStorage.php
@@ -16,7 +16,7 @@
  * the cache and delegates the read to the storage on a cache miss. It also
  * handles cache invalidation.
  */
-class CachedStorage implements StorageInterface {
+class CachedStorage implements StorageInterface, StorageCacheInterface {
 
   /**
    * The configuration storage to be cached.
@@ -33,6 +33,13 @@ class CachedStorage implements StorageInterface {
   protected $cache;
 
   /**
+   * List of listAll() prefixes with their results.
+   *
+   * @var array
+   */
+  protected static $listAllCache = array();
+
+  /**
    * Constructs a new CachedStorage controller.
    *
    * @param Drupal\Core\Config\StorageInterface $storage
@@ -80,6 +87,32 @@ public function read($name) {
   }
 
   /**
+   * Implements Drupal\Core\Config\StorageInterface::readMultiple().
+   */
+  public function readMultiple(array $names) {
+    $remaining_names = $names;
+    $list = array();
+    $cached_list = $this->cache->getMultiple($remaining_names);
+
+    // The cache backend removed names that were successfully loaded from the
+    // cache.
+    if (!empty($remaining_names)) {
+      $list = $this->storage->readMultiple($remaining_names);
+      // Cache configuration objects that were loaded from the storage.
+      foreach ($list as $name => $data) {
+        $this->cache->set($name, $data, CacheBackendInterface::CACHE_PERMANENT);
+      }
+    }
+
+    // Add the configuration objects from the cache to the list.
+    foreach ($cached_list as $name => $cache) {
+      $list[$name] = $cache->data;
+    }
+
+    return $list;
+  }
+
+  /**
    * Implements Drupal\Core\Config\StorageInterface::write().
    */
   public function write($name, array $data) {
@@ -87,6 +120,8 @@ public function write($name, array $data) {
       // While not all written data is read back, setting the cache instead of
       // just deleting it avoids cache rebuild stampedes.
       $this->cache->set($name, $data, CacheBackendInterface::CACHE_PERMANENT);
+      $this->cache->deleteTags(array('listAll' => TRUE));
+      static::$listAllCache = array();
       return TRUE;
     }
     return FALSE;
@@ -100,6 +135,8 @@ public function delete($name) {
     // rebuilding the cache before the storage is gone.
     if ($this->storage->delete($name)) {
       $this->cache->delete($name);
+      $this->cache->deleteTags(array('listAll' => TRUE));
+      static::$listAllCache = array();
       return TRUE;
     }
     return FALSE;
@@ -114,6 +151,8 @@ public function rename($name, $new_name) {
     if ($this->storage->rename($name, $new_name)) {
       $this->cache->delete($name);
       $this->cache->delete($new_name);
+      $this->cache->deleteTags(array('listAll' => TRUE));
+      static::$listAllCache = array();
       return TRUE;
     }
     return FALSE;
@@ -135,11 +174,24 @@ public function decode($raw) {
 
   /**
    * Implements Drupal\Core\Config\StorageInterface::listAll().
-   *
-   * Not supported by CacheBackendInterface.
    */
   public function listAll($prefix = '') {
-    return $this->storage->listAll($prefix);
+    // Check the static cache first.
+    if (!isset(static::$listAllCache[$prefix])) {
+
+      // The : character is not allowed in config file names, so this can not
+      // conflict.
+      // @todo: Maintain a single cache entry for this, similar to a cache
+      //   collector?
+      if ($cache = $this->cache->get('list:' . $prefix)) {
+        static::$listAllCache[$prefix] = $cache->data;
+      }
+      else {
+        static::$listAllCache[$prefix] = $this->storage->listAll($prefix);
+        $this->cache->set('list:' . $prefix, static::$listAllCache[$prefix], CacheBackendInterface::CACHE_PERMANENT, array('listAll' => TRUE));
+      }
+    }
+    return static::$listAllCache[$prefix];
   }
 
   /**
@@ -155,4 +207,11 @@ public function deleteAll($prefix = '') {
     }
     return FALSE;
   }
+
+  /**
+   * Clears the static list cache.
+   */
+  public function resetListCache() {
+    static::$listAllCache = array();
+  }
 }
diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php
index 740f316..02afbd1 100644
--- a/core/lib/Drupal/Core/Config/Config.php
+++ b/core/lib/Drupal/Core/Config/Config.php
@@ -108,6 +108,25 @@ public function init() {
   }
 
   /**
+   * Initializes a configuration object with pre-loaded data.
+   *
+   * @param array $data
+   *   Array of loaded data for this configuration object.
+   *
+   * @return Drupal\Core\Config\Config
+   *   The configuration object.
+   */
+  public function initWithData(array $data) {
+    $this->isLoaded = TRUE;
+    $this->overrides = array();
+    $this->isNew = FALSE;
+    $this->notify('init');
+    $this->replaceData($data);
+    $this->notify('load');
+    return $this;
+  }
+
+  /**
    * Returns the name of this configuration object.
    *
    * @return string
diff --git a/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php
index dd5df1e..fe9f4cb 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactory.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactory.php
@@ -85,6 +85,45 @@ public function get($name) {
   }
 
   /**
+   * Returns a list of configuration objects for a given names and context.
+   *
+   * This will pre-load all requested configuration objects does not create
+   * new configuration objects.
+   *
+   * @param array $names
+   *   List of names of configuration objects.
+   *
+   * @return array
+   *   List of successfully loaded configuration objects, keyed by name.
+   */
+  public function loadMultiple(array $names) {
+    $context = $this->getContext();
+
+    $list = array();
+    foreach ($names as $key => $name) {
+      $cache_key = $this->getCacheKey($name, $context);
+      // @todo: Deleted configuration stays in $this->cache, only return
+      //   config entities that are not new.
+      if (isset($this->cache[$cache_key]) && !$this->cache[$cache_key]->isNew()) {
+        $list[$name] = $this->cache[$cache_key];
+        unset($names[$key]);
+      }
+    }
+
+    // Pre-load remaining configuration files.
+    if (!empty($names)) {
+      $storage_data = $this->storage->readMultiple($names);
+      foreach ($storage_data as $name => $data) {
+        $cache_key = $this->getCacheKey($name, $context);
+        $this->cache[$cache_key] = new Config($name, $this->storage, $context);
+        $this->cache[$cache_key]->initWithData($data);
+        $list[$name] = $this->cache[$cache_key];
+      }
+    }
+    return $list;
+  }
+
+  /**
    * Resets and re-initializes configuration objects. Internal use only.
    *
    * @param string $name
@@ -104,6 +143,11 @@ public function reset($name = NULL) {
     else {
       $this->cache = array();
     }
+
+    // Clear the static list cache if supported by the storage.
+    if ($this->storage instanceof StorageCacheInterface) {
+      $this->storage->resetListCache();
+    }
     return $this;
   }
 
diff --git a/core/lib/Drupal/Core/Config/DatabaseStorage.php b/core/lib/Drupal/Core/Config/DatabaseStorage.php
index cc595a6..796bba1 100644
--- a/core/lib/Drupal/Core/Config/DatabaseStorage.php
+++ b/core/lib/Drupal/Core/Config/DatabaseStorage.php
@@ -86,6 +86,29 @@ public function read($name) {
   }
 
   /**
+   * Implements Drupal\Core\Config\StorageInterface::read().
+   *
+   *
+   * @param array $names
+   */
+  public function readMultiple(array $names) {
+    // There are situations, like in the installer, where we may attempt a
+    // read without actually having the database available. In this case,
+    // catch the exception and just return an empty array so the caller can
+    // handle it if need be.
+    $list = array();
+    try {
+      $list = $this->connection->query('SELECT name, data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name IN (:names)', array(':names' => $names), $this->options)->fetchAllKeyed();
+      foreach ($list as &$data) {
+        $data = $this->decode($data);
+      }
+    }
+    catch (Exception $e) {
+    }
+    return $list;
+  }
+
+  /**
    * Implements Drupal\Core\Config\StorageInterface::write().
    *
    * @throws PDOException
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
index 095114a..34b49d6 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -232,24 +232,20 @@ protected function buildQuery($ids, $revision_id = FALSE) {
     // Load all of the configuration entities.
     if ($ids === NULL) {
       $names = drupal_container()->get('config.storage')->listAll($prefix);
-      $result = array();
-      foreach ($names as $name) {
-        $config = config($name);
-        $result[$config->get($this->idKey)] = new $config_class($config->get(), $this->entityType);
-      }
-      return $result;
     }
     else {
-      $result = array();
+      $names = array();
       foreach ($ids as $id) {
         // Add the prefix to the ID to serve as the configuration object name.
-        $config = config($prefix . $id);
-        if (!$config->isNew()) {
-          $result[$id] = new $config_class($config->get(), $this->entityType);
-        }
+        $names[] = $prefix . $id;
       }
-      return $result;
     }
+
+    $result = array();
+    foreach (\Drupal::service('config.factory')->loadMultiple($names) as $config) {
+      $result[$config->get($this->idKey)] = new $config_class($config->get(), $this->entityType);
+    }
+    return $result;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php
index 6422e9d..7021c25 100644
--- a/core/lib/Drupal/Core/Config/FileStorage.php
+++ b/core/lib/Drupal/Core/Config/FileStorage.php
@@ -90,6 +90,21 @@ public function read($name) {
   }
 
   /**
+   * Implements Drupal\Core\Config\StorageInterface::readMultiple().
+   *
+   * @throws Symfony\Component\Yaml\Exception\ParseException
+   */
+  public function readMultiple(array $names) {
+    $list = array();
+    foreach ($names as $name) {
+      if ($data = $this->read($name)) {
+        $list[$name] = $data;
+      }
+    }
+    return $list;
+  }
+
+  /**
    * Implements Drupal\Core\Config\StorageInterface::write().
    *
    * @throws Symfony\Component\Yaml\Exception\DumpException
diff --git a/core/lib/Drupal/Core/Config/NullStorage.php b/core/lib/Drupal/Core/Config/NullStorage.php
index 336c111..2bf121d 100644
--- a/core/lib/Drupal/Core/Config/NullStorage.php
+++ b/core/lib/Drupal/Core/Config/NullStorage.php
@@ -38,6 +38,13 @@ public function read($name) {
   }
 
   /**
+   * Implements Drupal\Core\Config\StorageInterface::readMultiple().
+   */
+  public function readMultiple(array $names) {
+    return array();
+  }
+
+  /**
    * Implements Drupal\Core\Config\StorageInterface::write().
    */
   public function write($name, array $data) {
diff --git a/core/lib/Drupal/Core/Config/StorageCacheInterface.php b/core/lib/Drupal/Core/Config/StorageCacheInterface.php
new file mode 100644
index 0000000..026875d
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/StorageCacheInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\StorageCacheInterface.
+ */
+
+namespace Drupal\Core\Config;
+
+/**
+ * Defines an interface for cached configuration storage controllers.
+ */
+interface StorageCacheInterface {
+
+  /**
+   * Reset the static cache of the listAll() cache.
+   */
+  public function resetListCache();
+
+}
diff --git a/core/lib/Drupal/Core/Config/StorageInterface.php b/core/lib/Drupal/Core/Config/StorageInterface.php
index ceeecba..77eca0a 100644
--- a/core/lib/Drupal/Core/Config/StorageInterface.php
+++ b/core/lib/Drupal/Core/Config/StorageInterface.php
@@ -39,6 +39,18 @@ public function exists($name);
   public function read($name);
 
   /**
+   * Reads configuration data from the storage.
+   *
+   * @param ayrray $name
+   *   List of names of the configuration objects to load.
+   *
+   * @return array|bool
+   *   A list of the configuration data stored for the configuration object name
+   *   that could be loaded for the passed list of names.
+   */
+  public function readMultiple(array $names);
+
+  /**
    * Writes configuration data to the storage.
    *
    * @param string $name
diff --git a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php
new file mode 100644
index 0000000..6a08c79
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace Drupal\Tests\Core\Config;
+
+use Drupal\Tests\UnitTestCase;
+use Drupal\Core\Config\CachedStorage;
+use Drupal\Core\Cache\MemoryBackend;
+use Drupal\Core\Cache\NullBackend;
+use Drupal\Core\Cache\CacheBackendInterface;
+
+/**
+ * Tests the interaction of cache and file storage in CachedStorage.
+ *
+ * @group Config
+ */
+class CachedStorageTest extends UnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Config cached storage test',
+      'description' => 'Tests the interaction of cache and file storage in CachedStorage.',
+      'group' => 'Configuration'
+    );
+  }
+
+  /**
+   * Test listAll static cache.
+   */
+  public function testListAllStaticCache() {
+    $prefix = __FUNCTION__;
+    $storage = $this->getMock('Drupal\Core\Config\StorageInterface');
+    $storage->expects($this->once())
+      ->method('listAll')
+      ->with($prefix)
+      ->will($this->returnValue(array("$prefix.bar", "$prefix.baz")));
+
+    $cache = new NullBackend(__FUNCTION__);
+    $cachedStorage = new CachedStorage($storage, $cache);
+    $cachedStorage->listAll($prefix);
+    $cachedStorage->listAll($prefix);
+  }
+
+  /**
+   * Test CachedStorage::listAll() persistent cache.
+   */
+  public function testListAllPrimedPersistentCache() {
+    $prefix = __FUNCTION__;
+    $storage = $this->getMock('Drupal\Core\Config\StorageInterface');
+    $storage->expects($this->once())
+      ->method('listAll')
+      ->with($prefix)
+      ->will($this->returnValue(array("$prefix.bar", "$prefix.baz")));
+
+    $cache = new MemoryBackend(__FUNCTION__);
+    $cache->set('list:' . $prefix, array("$prefix.bar", "$prefix.baz"));
+    $cachedStorage = new CachedStorage($storage, $cache);
+    $cachedStorage->listAll($prefix);
+  }
+
+  /**
+   * Test that we don't fall through to file storage with a primed cache.
+   */
+  public function testGetMultipleOnPrimedCache() {
+    $configNames = array(
+      'foo.bar',
+      'baz.back',
+    );
+    $configCacheValues = array(
+      'foo.bar' => (object) array(
+        'data' => array('foo' => 'bar'),
+      ),
+      'baz.back' => (object) array(
+        'data' => array('foo' => 'bar'),
+      ),
+    );
+    $storage = $this->getMock('Drupal\Core\Config\StorageInterface');
+    $storage->expects($this->never())->method('readMultiple');
+    $cache = new MemoryBackend(__FUNCTION__);
+    foreach ($configCacheValues as $key => $value) {
+      $cache->set($key, $value);
+    }
+    $cachedStorage = new CachedStorage($storage, $cache);
+    $cachedStorage->readMultiple($configNames);
+  }
+
+  /**
+   * Test fall through to file storage on a cache miss.
+   */
+  public function testGetMultipleOnPartiallyPrimedCache() {
+    $configNames = array(
+      'foo.bar',
+      'baz.back',
+      'dididi.idiback',
+    );
+    $configCacheValues = array(
+      'foo.bar' => (object) array(
+        'data' => array('foo' => 'bar'),
+      ),
+      'baz.back' => (object) array(
+        'data' => array('foo' => 'bar'),
+      ),
+    );
+    $cache = new MemoryBackend(__FUNCTION__);
+    foreach ($configCacheValues as $key => $value) {
+      $cache->set($key, $value);
+    }
+
+    $storage = $this->getMock('Drupal\Core\Config\StorageInterface');
+    $storage->expects($this->once())
+      ->method('readMultiple')
+      ->with(array(2 => 'dididi.idiback'))
+      ->will($this->returnValue(array('dididi.idiback' => array('yo wtf'))));
+
+    $cachedStorage = new CachedStorage($storage, $cache);
+    $cachedStorage->readMultiple($configNames);
+  }
+}
