Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1038
diff -u -p -r1.1038 common.inc
--- includes/common.inc	2 Nov 2009 03:46:43 -0000	1.1038
+++ includes/common.inc	2 Nov 2009 20:56:49 -0000
@@ -4552,27 +4552,37 @@ function drupal_system_listing($mask, $d
  *   keyed array as described above.
  */
 function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
-  $hook = $type . '_alter';
-  foreach (module_implements($hook) as $module) {
-    $function = $module . '_' . $hook;
-    $function($data, $context1, $context2);
-  }
-  // Allow the theme to alter variables after the theme system has been
-  // initialized.
-  global $theme, $base_theme_info;
-  if (isset($theme)) {
-    $theme_keys = array();
-    foreach ($base_theme_info as $base) {
-      $theme_keys[] = $base->name;
-    }
-    $theme_keys[] = $theme;
-    foreach ($theme_keys as $theme_key) {
-      $function = $theme_key . '_' . $hook;
-      if (function_exists($function)) {
-        $function($data, $context1, $context2);
+  $functions = &drupal_static(__FUNCTION__, array());
+
+  // Some alter hooks are invoked many times per page request, so statically
+  // cache the list of functions to call, and on subsequent calls, iterate
+  // through them quickly.
+  if (!isset($functions[$type])) {
+    $functions[$type] = array();
+    $hook = $type . '_alter';
+    foreach (module_implements($hook) as $module) {
+      $functions[$type][] = $module . '_' . $hook;
+    }
+    // Allow the theme to alter variables after the theme system has been
+    // initialized.
+    global $theme, $base_theme_info;
+    if (isset($theme)) {
+      $theme_keys = array();
+      foreach ($base_theme_info as $base) {
+        $theme_keys[] = $base->name;
+      }
+      $theme_keys[] = $theme;
+      foreach ($theme_keys as $theme_key) {
+        $function = $theme_key . '_' . $hook;
+        if (function_exists($function)) {
+          $functions[$type][] = $function;
+        }
       }
     }
   }
+  foreach ($functions[$type] as $function) {
+    $function($data, $context1, $context2);
+  }
 }
 
 /**
Index: includes/module.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/module.inc,v
retrieving revision 1.164
diff -u -p -r1.164 module.inc
--- includes/module.inc	1 Nov 2009 22:10:07 -0000	1.164
+++ includes/module.inc	2 Nov 2009 20:56:50 -0000
@@ -383,6 +383,7 @@ function module_implements($hook, $sort 
     $implementations = array();
     cache_set('module_implements', array());
     drupal_static_reset('module_hook_info');
+    drupal_static_reset('drupal_alter');
     cache_clear_all('hook_info', 'cache');
     return;
   }
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.545
diff -u -p -r1.545 theme.inc
--- includes/theme.inc	2 Nov 2009 00:25:32 -0000	1.545
+++ includes/theme.inc	2 Nov 2009 20:56:50 -0000
@@ -85,6 +85,9 @@ function drupal_theme_initialize() {
     $ancestor = $themes[$ancestor]->base_theme;
   }
   _drupal_theme_initialize($themes[$theme], array_reverse($base_theme));
+
+  // Themes can have alter functions, so reset the drupal_alter() cache.
+  drupal_static_reset('drupal_alter');
 }
 
 /**
Index: modules/node/node.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.test,v
retrieving revision 1.51
diff -u -p -r1.51 node.test
--- modules/node/node.test	30 Oct 2009 22:33:35 -0000	1.51
+++ modules/node/node.test	2 Nov 2009 20:56:52 -0000
@@ -758,7 +758,8 @@ class NodeAccessRecordsUnitTest extends 
     $web_user = $this->drupalCreateUser(array('access content'));
     foreach ($operations as $op) {
       $grants = node_test_node_grants($op, $web_user);
-      $altered_grants = drupal_alter($grants, $web_user, $op);
+      $altered_grants = $grants;
+      drupal_alter('node_grants', $altered_grants, $web_user, $op);
       $this->assertNotEqual($grants, $altered_grants, t('Altered the %op grant for a user.', array('%op' => $op)));
     }
   }
