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:12:10 -0000
@@ -4552,24 +4554,49 @@ 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])) {
+    foreach ($functions[$type] as $function) {
+      $function($data, $context1, $context2);
+    }
+  }
+  else {
+    global $theme, $base_theme_info;
+    // Only save the function list to the static variable after the theme
+    // has initialized, because the theme may add more functions, and it's
+    // unlikely that an alter hook will be called enough times prior to
+    // theme initialization for it to be worthwhile to statically cache and
+    // then reset.
+    if (isset($theme)) {
+      $functions[$type] = array();
+    }
+    $hook = $type . '_alter';
+
+    foreach (module_implements($hook) as $module) {
+      $function = $module . '_' . $hook;
+      $function($data, $context1, $context2);
+      if (isset($functions[$type])) {
+        $functions[$type][] = $function;
+      }
+    }
+    // Allow the theme to alter variables after the theme system has been
+    // initialized.
+    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[$type][] = $function;
+        }
       }
     }
   }
 /**
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:12:11 -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: 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:12:13 -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)));
     }
   }
