diff --git a/core/lib/Drupal/Core/Cache/CacheableMetadata.php b/core/lib/Drupal/Core/Cache/CacheableMetadata.php
index 0a22f56..a0ec249 100644
--- a/core/lib/Drupal/Core/Cache/CacheableMetadata.php
+++ b/core/lib/Drupal/Core/Cache/CacheableMetadata.php
@@ -140,9 +140,38 @@ public function setCacheMaxAge($max_age) {
    */
   public function merge(CacheableMetadata $other) {
     $result = new static();
-    $result->contexts = Cache::mergeContexts($this->contexts, $other->contexts);
-    $result->tags = Cache::mergeTags($this->tags, $other->tags);
-    $result->maxAge = Cache::mergeMaxAges($this->maxAge, $other->maxAge);
+
+    // This is called many times per request, so avoid merging unless absolutely
+    // necessary.
+    if (empty($this->contexts)) {
+      $result->contexts = $other->contexts;
+    }
+    elseif (empty($other->contexts)) {
+      $result->contexts = $this->contexts;
+    }
+    else {
+      $result->contexts = Cache::mergeContexts($this->contexts, $other->contexts);
+    }
+
+    if (empty($this->tags)) {
+      $result->tags = $other->tags;
+    }
+    elseif (empty($other->tags)) {
+      $result->tags = $this->tags;
+    }
+    else {
+      $result->tags = Cache::mergeTags($this->tags, $other->tags);
+    }
+
+    if ($this->maxAge === Cache::PERMANENT) {
+      $result->maxAge = $other->maxAge;
+    }
+    elseif ($other->maxAge === Cache::PERMANENT) {
+      $result->maxAge = $this->maxAge;
+    }
+    else {
+      Cache::mergeMaxAges($this->maxAge, $other->maxAge);
+    }
     return $result;
   }
 
diff --git a/core/lib/Drupal/Core/Render/BubbleableMetadata.php b/core/lib/Drupal/Core/Render/BubbleableMetadata.php
index 93a9310..f2b83e8 100644
--- a/core/lib/Drupal/Core/Render/BubbleableMetadata.php
+++ b/core/lib/Drupal/Core/Render/BubbleableMetadata.php
@@ -42,9 +42,29 @@ class BubbleableMetadata extends CacheableMetadata {
    */
   public function merge(CacheableMetadata $other) {
     $result = parent::merge($other);
+
+    // This is called many times per request, so avoid merging unless absolutely
+    // necessary.
     if ($other instanceof BubbleableMetadata) {
-      $result->attached = \Drupal::service('renderer')->mergeAttachments($this->attached, $other->attached);
-      $result->postRenderCache = NestedArray::mergeDeep($this->postRenderCache, $other->postRenderCache);
+      if (empty($this->attached)) {
+        $result->attached = $other->attached;
+      }
+      elseif (empty($other->attached)) {
+        $result->attached = $this->attached;
+      }
+      else {
+        $result->attached = \Drupal::service('renderer')->mergeAttachments($this->attached, $other->attached);
+      }
+
+      if (empty($this->postRenderCache)) {
+        $result->postRenderCache = $other->postRenderCache;
+      }
+      elseif (empty($other->postRenderCache)) {
+        $result->postRenderCache = $this->postRenderCache;
+      }
+      else {
+        $result->postRenderCache = NestedArray::mergeDeep($this->postRenderCache, $other->postRenderCache);
+      }
     }
     return $result;
   }
