diff --git a/core/lib/Drupal/Core/Block/BlockBase.php b/core/lib/Drupal/Core/Block/BlockBase.php
index 262fd0c..cc703cd 100644
--- a/core/lib/Drupal/Core/Block/BlockBase.php
+++ b/core/lib/Drupal/Core/Block/BlockBase.php
@@ -108,6 +108,7 @@ public function defaultConfiguration() {
    */
   public function setConfigurationValue($key, $value) {
     $this->configuration[$key] = $value;
+    return $this;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Block/BlockPluginInterface.php b/core/lib/Drupal/Core/Block/BlockPluginInterface.php
index 0a58b33..0440989 100644
--- a/core/lib/Drupal/Core/Block/BlockPluginInterface.php
+++ b/core/lib/Drupal/Core/Block/BlockPluginInterface.php
@@ -78,6 +78,8 @@ public function build();
    * @param mixed $value
    *   The value to set for the provided key.
    *
+   * @return $this
+   *
    * @todo This doesn't belong here. Move this into a new base class in
    *   http://drupal.org/node/1764380.
    * @todo This does not set a value in \Drupal::config(), so the name is confusing.
diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml
index df4d0d7..7ba57c2 100644
--- a/core/modules/block/block.services.yml
+++ b/core/modules/block/block.services.yml
@@ -24,4 +24,4 @@ services:
       - { name: 'event_subscriber' }
   block.repository:
     class: Drupal\block\BlockRepository
-    arguments: ['@entity.manager', '@theme.manager', '@context.handler']
+    arguments: ['@entity.manager', '@theme.manager', '@context.handler', '@cache.data']
diff --git a/core/modules/block/src/BlockRepository.php b/core/modules/block/src/BlockRepository.php
index 485a66f..1ae94c3 100644
--- a/core/modules/block/src/BlockRepository.php
+++ b/core/modules/block/src/BlockRepository.php
@@ -7,9 +7,11 @@
 
 namespace Drupal\block;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Plugin\Context\ContextHandlerInterface;
 use Drupal\Core\Theme\ThemeManagerInterface;
+use Drupal\Core\Cache\CacheBackendInterface;
 
 /**
  * Provides a repository for Block config entities.
@@ -31,6 +33,13 @@ class BlockRepository implements BlockRepositoryInterface {
   protected $themeManager;
 
   /**
+   * The cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cacheBackend;
+
+  /**
    * Constructs a new BlockRepository.
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
@@ -39,11 +48,13 @@ class BlockRepository implements BlockRepositoryInterface {
    *   The theme manager.
    * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
    *   The plugin context handler.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
    */
-  public function __construct(EntityManagerInterface $entity_manager, ThemeManagerInterface $theme_manager, ContextHandlerInterface $context_handler) {
+  public function __construct(EntityManagerInterface $entity_manager, ThemeManagerInterface $theme_manager, ContextHandlerInterface $context_handler, CacheBackendInterface $cache_backend) {
     $this->blockStorage = $entity_manager->getStorage('block');
     $this->themeManager = $theme_manager;
     $this->contextHandler = $context_handler;
+    $this->cacheBackend = $cache_backend;
   }
 
   /**
@@ -74,7 +85,18 @@ public function getVisibleBlocksPerRegion(array $contexts) {
     $empty = array_fill_keys(array_keys($this->getRegionNames()), array());
 
     $full = array();
-    foreach ($this->blockStorage->loadByProperties(array('theme' => $this->getTheme())) as $block_id => $block) {
+    $theme = $this->getTheme();
+    $cid = 'block_list:' . $theme;
+    if ($cached = $this->cacheBackend->get($cid)) {
+      $blocks = $cached->data;
+    }
+    else {
+      $blocks = $this->blockStorage->loadByProperties(['theme' => $theme]);
+      // Add the block config entity list cache tag, so that this is invalidated
+      // when blocks change.
+      $this->cacheBackend->set($cid, $blocks, Cache::PERMANENT, ['config:block_list']);
+    }
+    foreach ($blocks as $block_id => $block) {
       /** @var \Drupal\block\BlockInterface $block */
       // Set the contexts on the block before checking access.
       if ($block->setContexts($contexts)->access('view')) {
diff --git a/core/modules/block/src/Tests/BlockSystemBrandingTest.php b/core/modules/block/src/Tests/BlockSystemBrandingTest.php
index 57ebc85..badfc42 100644
--- a/core/modules/block/src/Tests/BlockSystemBrandingTest.php
+++ b/core/modules/block/src/Tests/BlockSystemBrandingTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\block\Tests;
 
+use Drupal\block\Entity\Block;
+
 /**
  * Tests branding block display.
  *
@@ -54,9 +56,11 @@ public function testSystemBrandingSettings() {
     $this->assertCacheTag('config:system.site');
 
     // Turn just the logo off.
-    $this->config('block.block.site-branding')
-      ->set('settings.use_site_logo', 0)
-      ->save();
+    /** @var \Drupal\block\BlockInterface $block */
+    $block = Block::load('site-branding');
+    $block->getPlugin()->setConfigurationValue('use_site_logo', FALSE);
+    $block->save();
+
     $this->drupalGet('');
     $site_logo_element = $this->xpath($site_logo_xpath);
     $site_name_element = $this->xpath($site_name_xpath);
@@ -68,10 +72,11 @@ public function testSystemBrandingSettings() {
     $this->assertCacheTag('config:system.site');
 
     // Turn just the site name off.
-    $this->config('block.block.site-branding')
-      ->set('settings.use_site_logo', 1)
-      ->set('settings.use_site_name', 0)
-      ->save();
+    $block->getPlugin()
+      ->setConfigurationValue('use_site_logo', TRUE)
+      ->setConfigurationValue('use_site_name', FALSE);
+    $block->save();
+
     $this->drupalGet('');
     $site_logo_element = $this->xpath($site_logo_xpath);
     $site_name_element = $this->xpath($site_name_xpath);
@@ -83,10 +88,11 @@ public function testSystemBrandingSettings() {
     $this->assertCacheTag('config:system.site');
 
     // Turn just the site slogan off.
-    $this->config('block.block.site-branding')
-      ->set('settings.use_site_name', 1)
-      ->set('settings.use_site_slogan', 0)
-      ->save();
+    $block->getPlugin()
+      ->setConfigurationValue('use_site_name', TRUE)
+      ->setConfigurationValue('use_site_slogan', FALSE);
+    $block->save();
+
     $this->drupalGet('');
     $site_logo_element = $this->xpath($site_logo_xpath);
     $site_name_element = $this->xpath($site_name_xpath);
@@ -98,10 +104,11 @@ public function testSystemBrandingSettings() {
     $this->assertCacheTag('config:system.site');
 
     // Turn the site name and the site slogan off.
-    $this->config('block.block.site-branding')
-      ->set('settings.use_site_name', 0)
-      ->set('settings.use_site_slogan', 0)
-      ->save();
+    $block->getPlugin()
+      ->setConfigurationValue('use_site_name', FALSE)
+      ->setConfigurationValue('use_site_slogan', FALSE);
+    $block->save();
+
     $this->drupalGet('');
     $site_logo_element = $this->xpath($site_logo_xpath);
     $site_name_element = $this->xpath($site_name_xpath);
diff --git a/core/modules/block/tests/src/Unit/BlockRepositoryTest.php b/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
index f589e82..0548bdc 100644
--- a/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
+++ b/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
@@ -8,6 +8,8 @@
 namespace Drupal\Tests\block\Unit;
 
 use Drupal\Core\Block\BlockPluginInterface;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
 use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Tests\UnitTestCase;
 
@@ -38,6 +40,13 @@ class BlockRepositoryTest extends UnitTestCase {
   protected $contextHandler;
 
   /**
+   * The cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cacheBackend;
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
@@ -61,9 +70,10 @@ protected function setUp() {
     $entity_manager->expects($this->any())
       ->method('getStorage')
       ->willReturn($this->blockStorage);
+    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
 
     $this->blockRepository = $this->getMockBuilder('Drupal\block\BlockRepository')
-      ->setConstructorArgs([$entity_manager, $theme_manager, $this->contextHandler])
+      ->setConstructorArgs([$entity_manager, $theme_manager, $this->contextHandler, $this->cacheBackend])
       ->setMethods(['getRegionNames'])
       ->getMock();
     $this->blockRepository->expects($this->once())
@@ -102,13 +112,8 @@ public function testGetVisibleBlocksPerRegion(array $blocks_config, array $expec
       ->method('loadByProperties')
       ->with(['theme' => $this->theme])
       ->willReturn($blocks);
-    $result = [];
-    foreach ($this->blockRepository->getVisibleBlocksPerRegion([]) as $region => $resulting_blocks) {
-      $result[$region] = [];
-      foreach ($resulting_blocks as $plugin_id => $block) {
-        $result[$region][] = $plugin_id;
-      }
-    }
+
+    $result = $this->getAllVisibleBlocks([]);
     $this->assertSame($result, $expected_blocks);
   }
 
@@ -147,6 +152,45 @@ public function providerBlocksConfig() {
    * @covers ::getVisibleBlocksPerRegion
    */
   public function testGetVisibleBlocksPerRegionWithContext() {
+    $contexts = [];
+    $contexts[] = new Context(new ContextDefinition('entity:user', 'Current user'));
+
+    $block = $this->getMock('Drupal\block\BlockInterface');
+    $block->expects($this->once())
+      ->method('setContexts')
+      ->with($contexts)
+      ->willReturnSelf();
+    $block->expects($this->once())
+      ->method('access')
+      ->willReturn(TRUE);
+    $block->expects($this->once())
+      ->method('getRegion')
+      ->willReturn('top');
+    $blocks['block_id'] = $block;
+
+    $this->blockStorage->expects($this->once())
+      ->method('loadByProperties')
+      ->with(['theme' => $this->theme])
+      ->willReturn($blocks);
+
+    $result = $this->getAllVisibleBlocks($contexts);
+    $expected = [
+      'top' => [
+        'block_id',
+      ],
+      'center' => [],
+      'bottom' => [],
+    ];
+    $this->assertSame($expected, $result);
+  }
+
+  /**
+   * @covers ::getVisibleBlocksPerRegion
+   */
+  public function testGetVisibleBlocksPerRegionFromCache() {
+    $this->blockStorage->expects($this->never())
+      ->method('loadByProperties');
+
     $block = $this->getMock('Drupal\block\BlockInterface');
     $block->expects($this->once())
       ->method('setContexts')
@@ -159,18 +203,12 @@ public function testGetVisibleBlocksPerRegionWithContext() {
       ->willReturn('top');
     $blocks['block_id'] = $block;
 
-    $contexts = [];
-    $this->blockStorage->expects($this->once())
-      ->method('loadByProperties')
-      ->with(['theme' => $this->theme])
-      ->willReturn($blocks);
-    $result = [];
-    foreach ($this->blockRepository->getVisibleBlocksPerRegion($contexts) as $region => $resulting_blocks) {
-      $result[$region] = [];
-      foreach ($resulting_blocks as $plugin_id => $block) {
-        $result[$region][] = $plugin_id;
-      }
-    }
+    $this->cacheBackend->expects($this->once())
+      ->method('get')
+      ->with('block_list:' . $this->theme)
+      ->willReturn((object) ['data' => $blocks]);
+
+    $result = $this->getAllVisibleBlocks([]);
     $expected = [
       'top' => [
         'block_id',
@@ -181,6 +219,20 @@ public function testGetVisibleBlocksPerRegionWithContext() {
     $this->assertSame($expected, $result);
   }
 
+  /**
+   * Calls getVisibleBlocksPerRegion() for a set of contexts.
+   */
+  protected function getAllVisibleBlocks(array $contexts) {
+    $result = [];
+    foreach ($this->blockRepository->getVisibleBlocksPerRegion($contexts) as $region => $resulting_blocks) {
+      $result[$region] = [];
+      foreach ($resulting_blocks as $plugin_id => $block) {
+        $result[$region][] = $plugin_id;
+      }
+    }
+    return $result;
+  }
+
 }
 
 interface TestContextAwareBlockInterface extends BlockPluginInterface, ContextAwarePluginInterface {
