diff --git a/reviewboard/actions/registry.py b/reviewboard/actions/registry.py
index 13cfb10e0290487cc4d23e897e920d748e505f9b..dc691af5f715aee8a1b8a299835c6c115b89dc58 100644
--- a/reviewboard/actions/registry.py
+++ b/reviewboard/actions/registry.py
@@ -35,11 +35,25 @@ class ActionsRegistry(OrderedRegistry):
 
     MAX_DEPTH = 2
 
+    ######################
+    # Instance variables #
+    ######################
+
+    #: Action lookup by attachment point.
+    #:
+    #: Version Added:
+    #:     7.1
+    _by_attachment_point: dict[str, dict[str, BaseAction]]
+
+    #: Actions deferring registration until a dependency is registered.
+    _deferred_registrations: list[BaseAction]
+
     def __init__(self) -> None:
         """Initialize the registry."""
         super().__init__()
 
-        self._deferred_registrations: List[BaseAction] = []
+        self._by_attachment_point = {}
+        self._deferred_registrations = []
 
     def get_defaults(self) -> Iterator[BaseAction]:
         """Yield the built-in actions.
@@ -172,7 +186,8 @@ class ActionsRegistry(OrderedRegistry):
             reviewboard.actions.errors.DepthLimitExceededError:
                 The action was nested too deeply.
         """
-        assert action.action_id is not None
+        action_id = action.action_id
+        assert action_id is not None
 
         parent: Optional[BaseAction]
 
@@ -182,7 +197,7 @@ class ActionsRegistry(OrderedRegistry):
             if parent is None:
                 logger.warning('Deferring registration of action %s because '
                                'parent action %s is not yet registered.',
-                               action.action_id, action.parent_id)
+                               action_id, action.parent_id)
                 self._deferred_registrations.append(action)
                 return
 
@@ -194,6 +209,23 @@ class ActionsRegistry(OrderedRegistry):
 
         super().register(action)
 
+        # Store this by attachment point, for quick lookup.
+        attachment = action.attachment
+
+        if attachment:
+            if isinstance(attachment, str):
+                attachment = [attachment]
+
+            for attachment in attachment:
+                try:
+                    attachment_actions = self._by_attachment_point[attachment]
+                except KeyError:
+                    attachment_actions = {}
+                    self._by_attachment_point[attachment] = attachment_actions
+
+                attachment_actions[action_id] = action
+
+        # Establish the parent/child relationship between actions.
         if parent:
             action.parent_action = parent
             parent.child_actions.append(action)
@@ -206,7 +238,7 @@ class ActionsRegistry(OrderedRegistry):
         # referring to the same parent, so we have to iterate through this
         # entire list.
         for deferred in self._deferred_registrations:
-            if deferred.parent_id == action.action_id:
+            if deferred.parent_id == action_id:
                 self.register(deferred)
                 self._deferred_registrations.remove(deferred)
 
@@ -224,10 +256,22 @@ class ActionsRegistry(OrderedRegistry):
             djblets.registries.errors.ItemLookupError:
                 Raised if the item is not found in the registry.
         """
-        if action.parent_action:
-            action.parent_action.child_actions.remove(action)
+        if (parent_action := action.parent_action):
+            parent_action.child_actions.remove(action)
             action.parent_action = None
 
+        # Remove the per-attachment lookups.
+        attachment = action.attachment
+
+        if attachment:
+            if isinstance(attachment, str):
+                attachment = [attachment]
+
+            for attachment in attachment:
+                assert action.action_id
+                self._by_attachment_point[attachment].pop(action.action_id,
+                                                          None)
+
         super().unregister(action)
 
     def get_for_attachment(
@@ -252,10 +296,12 @@ class ActionsRegistry(OrderedRegistry):
         """
         self.populate()
 
-        for action in self:
-            if (action.attachment == attachment and
-                (action.parent_id is None or include_children)):
-                yield action
+        attachment_actions = self._by_attachment_point.get(attachment)
+
+        if attachment_actions:
+            for action in attachment_actions.values():
+                if action.parent_id is None or include_children:
+                    yield action
 
     def get_children(
         self,
diff --git a/reviewboard/actions/tests/test_registry.py b/reviewboard/actions/tests/test_registry.py
index 0b5d57865c5b7fde6d55605b5b68ed0a7411404f..5c7ddcb9d07b9af234bcc14479c54cab0ee4e5a4 100644
--- a/reviewboard/actions/tests/test_registry.py
+++ b/reviewboard/actions/tests/test_registry.py
@@ -4,6 +4,10 @@ Version Added:
     6.0
 """
 
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
 from djblets.registries.errors import AlreadyRegisteredError
 
 from reviewboard.actions import (AttachmentPoint,
@@ -11,8 +15,12 @@ from reviewboard.actions import (AttachmentPoint,
                                  BaseMenuAction,
                                  actions_registry)
 from reviewboard.actions.errors import DepthLimitExceededError
+from reviewboard.actions.registry import ActionsRegistry
 from reviewboard.testing import TestCase
 
+if TYPE_CHECKING:
+    from collections.abc import Iterator
+
 
 class TestAction(BaseAction):
     action_id = 'test'
@@ -47,6 +55,23 @@ class TooDeeplyNestedAction(BaseAction):
     parent_id = 'nested-2-menu-action'
 
 
+class _TestActionsRegistry(ActionsRegistry):
+    """Empty actions registry for testing purposes.
+
+    Version Added:
+        7.1
+    """
+
+    def get_defaults(self) -> Iterator[BaseAction]:
+        """Return an empty set of defaults.
+
+        Yields:
+            reviewboard.actions.base.BaseAction:
+            Each action (but none, really).
+        """
+        yield from []
+
+
 class ActionsRegistryTests(TestCase):
     """Unit tests for the actions registry.
 
@@ -70,20 +95,78 @@ class ActionsRegistryTests(TestCase):
 
     def test_register(self) -> None:
         """Testing ActionsRegistry.register"""
-        actions_registry.register(self.test_action)
+        test_action = self.test_action
+
+        actions_registry = _TestActionsRegistry()
+        actions_registry.register(test_action)
 
         with self.assertRaises(AlreadyRegisteredError):
-            actions_registry.register(self.test_action)
+            actions_registry.register(test_action)
+
+        self.assertEqual(
+            actions_registry._by_attachment_point,
+            {
+                'review-request': {
+                    'test': test_action,
+                },
+            })
+
+    def test_unregister(self) -> None:
+        """Testing ActionsRegistry.unregister"""
+        test_action = self.test_action
+
+        actions_registry = _TestActionsRegistry()
+        actions_registry.register(test_action)
+
+        self.assertEqual(
+            actions_registry._by_attachment_point,
+            {
+                'review-request': {
+                    'test': test_action,
+                },
+            })
+
+        actions_registry.unregister(test_action)
+
+        self.assertEqual(
+            actions_registry._by_attachment_point,
+            {
+                'review-request': {},
+            })
 
     def test_get_for_attachment(self) -> None:
         """Testing ActionsRegistry.get_for_attachment"""
-        actions_registry.register(self.test_action)
-        actions_registry.register(self.test_header_action)
+        actions_registry = _TestActionsRegistry()
 
-        header_actions = list(actions_registry.get_for_attachment(
-            AttachmentPoint.HEADER))
-        self.assertIn(self.test_header_action, header_actions)
-        self.assertNotIn(self.test_action, header_actions)
+        test_action = self.test_action
+        test_header_action = self.test_header_action
+
+        actions_registry.register(test_action)
+        actions_registry.register(test_header_action)
+
+        self.assertEqual(
+            actions_registry._by_attachment_point,
+            {
+                'header': {
+                    'header-action': test_header_action,
+                },
+                'review-request': {
+                    'test': test_action,
+                },
+            })
+
+        self.assertEqual(
+            list(actions_registry.get_for_attachment(AttachmentPoint.HEADER)),
+            [
+                test_header_action,
+            ])
+
+        self.assertEqual(
+            list(actions_registry.get_for_attachment(
+                AttachmentPoint.REVIEW_REQUEST)),
+            [
+                test_action,
+            ])
 
     def test_menu(self) -> None:
         """Testing ActionsRegistry with menu actions"""
