diff --git a/includes/common.inc b/includes/common.inc
index 05af5a7..76c3fe1 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -1623,6 +1623,9 @@ function drupal_page_footer() {
   }
 
   module_invoke_all('exit');
+
+  // Update the hook implementation cache.
+  module_implements('', FALSE, FALSE, TRUE);
 }
 
 /**
diff --git a/includes/module.inc b/includes/module.inc
index bc4fbea..dc23f67 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -242,15 +242,15 @@ function module_load_install($module) {
 
 /**
  * Load a module include file.
- * 
+ *
  * Examples:
  * @code
  *   // Load node.admin.inc from the node module.
  *   module_load_include('inc', 'node', 'node.admin');
  *   // Load content_types.inc from the node module.
- *   module_load_include('inc', 'node', 'content_types');  
+ *   module_load_include('inc', 'node', 'content_types');
  * @endcode
- * 
+ *
  * Do not use this function to load an install file. Use module_load_install()
  * instead.
  *
@@ -259,7 +259,7 @@ function module_load_install($module) {
  * @param $module
  *   The module to which the include file belongs.
  * @param $name
- *   Optionally, specify the base file name (without the $type extension). 
+ *   Optionally, specify the base file name (without the $type extension).
  *   If not set, $module is used.
  */
 function module_load_include($type, $module, $name = NULL) {
@@ -405,22 +405,62 @@ function module_hook($module, $hook) {
  * @param $sort
  *   By default, modules are ordered by weight and filename, settings this option
  *   to TRUE, module list will be ordered by module name.
- * @param $refresh
+ * @param $reset
  *   For internal use only: Whether to force the stored list of hook
  *   implementations to be regenerated (such as after enabling a new module,
  *   before processing hook_enable).
+ * @param $write_cache
+ *   For internal use only: Update the persistent cache of hook implementations.
  * @return
  *   An array with the names of the modules which are implementing this hook.
  */
-function module_implements($hook, $sort = FALSE, $refresh = FALSE) {
+function module_implements($hook, $sort = FALSE, $reset = FALSE, $write_cache = FALSE) {
   static $implementations;
-
-  if ($refresh) {
+  static $verified;
+
+  // We maintain a persistent cache of hook implementations in addition to the
+  // static cache to avoid looping through every module and every hook on each
+  // request. Benchmarks show that the benefit of this caching outweighs the
+  // additional database hit even when using the default database caching
+  // backend and only a small number of modules are enabled. The cost of the
+  // cache_get() is more or less constant and reduced further when non-database
+  // caching backends are used, so there will be more significant gains when a
+  // large number of modules are installed or hooks invoked, since this can
+  // quickly lead to module_hook() being called several thousand times
+  // per request.
+  if ($reset) {
     $implementations = array();
+    $verified = array();
+    cache_set('module_implements', array());
+    return;
+  }
+
+  if ($write_cache) {
+    // Check whether we should write the cache. We do not want to cache hooks
+    // which are only invoked on HTTP POST requests since these do not need to
+    // be optimized as tightly, and not doing so keeps the cache entry smaller.
+    if (isset($implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) {
+      unset($implementations['#write_cache']);
+      cache_set('module_implements', $implementations);
+    }
     return;
   }
 
+  // Fetch implementations from cache.
+  if (empty($implementations)) {
+    $cache = cache_get('module_implements');
+    if (!$cache) {
+      $implementations = array();
+    }
+    else {
+      $implementations = $cache->data;
+    }
+  }
+
   if (!isset($implementations[$hook])) {
+    // The hook is not cached, so ensure that whether or not it has
+    // implementations, that the cache is updated at the end of the request.
+    $implementations['#write_cache'] = TRUE;
     $implementations[$hook] = array();
     $list = module_list(FALSE, TRUE, $sort);
     foreach ($list as $module) {
@@ -429,6 +469,20 @@ function module_implements($hook, $sort = FALSE, $refresh = FALSE) {
       }
     }
   }
+  elseif (empty($verified[$hook])) {
+    foreach ($implementations[$hook] as $key => $module) {
+      // It is possible that a module removed a hook implementation without the
+      // implementations cache being rebuilt yet, so we check module_hook() on
+      // each request to avoid undefined function errors.
+      if (!module_hook($module, $hook)) {
+        // Clear out the stale implementation from the cache and force a cache
+        // refresh to forget about no longer existing hook implementations.
+        unset($implementations[$hook][$key]);
+        $implementations['#write_cache'] = TRUE;
+      }
+    }
+    $verified[$hook] = TRUE;
+  }
 
   // The explicit cast forces a copy to be made. This is needed because
   // $implementations[$hook] is only a reference to an element of
diff --git a/install.php b/install.php
index 8cac289..3699f00 100644
--- a/install.php
+++ b/install.php
@@ -39,6 +39,18 @@ function install_main() {
   drupal_load('module', 'system');
   drupal_load('module', 'filter');
 
+  // Load the cache infrastructure using a "fake" cache implementation that
+  // does not attempt to write to the database. We need this during the initial
+  // part of the installer because the database is not available yet. We
+  // continue to use it even when the database does become available, in order
+  // to preserve consistency between interactive and command-line installations
+  // (the latter complete in one page request and therefore are forced to
+  // continue using the cache implementation they started with) and also
+  // because any data put in the cache during the installer is inherently
+  // suspect, due to the fact that Drupal is not fully set up yet.
+  require_once './includes/cache-install.inc';
+  $conf['cache_inc'] = './includes/cache-install.inc';
+
   // Install profile chosen, set the global immediately.
   // This needs to be done before the theme cache gets 
   // initialized in drupal_maintenance_theme().
@@ -53,12 +65,6 @@ function install_main() {
   $verify = install_verify_settings();
 
   if ($verify) {
-    // Since we have a database connection, we use the normal cache system.
-    // This is important, as the installer calls into the Drupal system for
-    // the clean URL checks, so we should maintain the cache properly.
-    require_once './includes/cache.inc';
-    $conf['cache_inc'] = './includes/cache.inc';
-
     // Establish a connection to the database.
     require_once './includes/database.inc';
     db_set_active();
@@ -70,13 +76,6 @@ function install_main() {
     }
   }
   else {
-    // Since no persistent storage is available yet, and functions that check
-    // for cached data will fail, we temporarily replace the normal cache
-    // system with a stubbed-out version that short-circuits the actual
-    // caching process and avoids any errors.
-    require_once './includes/cache-install.inc';
-    $conf['cache_inc'] = './includes/cache-install.inc';
-
     $task = NULL;
   }
 
@@ -807,17 +806,14 @@ if (Drupal.jsEnabled) {
 
   // The end of the install process. Remember profile used.
   if ($task == 'done') {
-    // Rebuild menu to get content type links registered by the profile,
-    // and possibly any other menu items created through the tasks.
-    menu_rebuild();
+    // Flush all caches to ensure that any full bootstraps during the installer
+    // do not leave stale cached data, and that any content types or other items
+    // registered by the install profile are registered correctly.
+    drupal_flush_all_caches();
 
     // Register actions declared by any modules.
     actions_synchronize();
 
-    // Randomize query-strings on css/js files, to hide the fact that
-    // this is a new install, not upgraded yet.
-    _drupal_flush_css_js();
-
     variable_set('install_profile', $profile);
   }
 
diff --git a/update.php b/update.php
index 38fa9b7..bc0566d 100644
--- a/update.php
+++ b/update.php
@@ -594,7 +594,7 @@ require_once './includes/bootstrap.inc';
 $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
 if (empty($op)) {
   // Minimum load of components.
-  drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
+  drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
 
   require_once './includes/install.inc';
   require_once './includes/file.inc';
@@ -605,6 +605,8 @@ if (empty($op)) {
   $module_list['system']['filename'] = 'modules/system/system.module';
   $module_list['filter']['filename'] = 'modules/filter/filter.module';
   module_list(TRUE, FALSE, FALSE, $module_list);
+  module_implements('', FALSE, TRUE);
+
   drupal_load('module', 'system');
   drupal_load('module', 'filter');
 
