diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# effectful-core-2.7.1.1 (2026-08-24)
+* Fix a performance regression introduced in 2.7.0.0 that increased the
+  per-operation overhead of dynamically dispatched effects.
+
 # effectful-core-2.7.1.0 (2026-08-24)
 * Export `seqForkUnliftIO` and add `unsafeSeqForkUnliftIO` in
   `Effectful.Dispatch.Static` for the `SeqForkUnlift` strategy.
diff --git a/effectful-core.cabal b/effectful-core.cabal
--- a/effectful-core.cabal
+++ b/effectful-core.cabal
@@ -1,7 +1,7 @@
 cabal-version:      3.8
 build-type:         Simple
 name:               effectful-core
-version:            2.7.1.0
+version:            2.7.1.1
 license:            BSD-3-Clause
 license-file:       LICENSE
 category:           Control
@@ -51,6 +51,10 @@
 library
     import:         language
 
+    -- -O2 enables -ffast-pap-calls, without which the handler invocation in
+    -- send and passthrough (an unknown call with 5 pointers and the State#
+    -- token that matches no precompiled RTS apply pattern) allocates an
+    -- intermediate PAP on every operation of a dynamically dispatched effect.
     ghc-options:    -O2
 
     build-depends:    base                >= 4.18      && < 5
diff --git a/src/Effectful/Dispatch/Dynamic.hs b/src/Effectful/Dispatch/Dynamic.hs
--- a/src/Effectful/Dispatch/Dynamic.hs
+++ b/src/Effectful/Dispatch/Dynamic.hs
@@ -1210,6 +1210,21 @@
 ----------------------------------------
 -- Helpers
 
+-- Note [NOINLINE on the Impl functions]
+--
+-- The public wrappers must inline at call sites, because that's the only place
+-- the callstack-thawing adapter around the handler (see 'HandlerImpl') can
+-- fuse with a concrete handler. If it doesn't, the adapter is stored in the
+-- 'Handler' as a closure over an unknown function and every 'send' pays for an
+-- extra unknown call, CallStack allocation and generic apply of a PAP.
+--
+-- The wrappers have no INLINE pragmas, so they expose their optimized RHS as
+-- the unfolding and GHC inlines them based on its size. NOINLINE below keeps
+-- the bodies of the Impl functions out of these unfoldings, guaranteeing that
+-- they stay small. Historically reinterpretImpl had an INLINE pragma instead
+-- and a minor addition to it grew the wrappers of reinterpret past the
+-- inlining threshold, silently causing exactly this regression.
+
 interpretImpl
   :: (HasCallStack, DispatchOf e ~ Dynamic)
   => Eff (e : es) a
@@ -1217,7 +1232,7 @@
   -> Eff      es  a
 interpretImpl action handlerImpl = unsafeEff $ \es -> do
   (`unEff` es) $ runHandler (Handler es handlerImpl) action
-{-# INLINE interpretImpl #-}
+{-# NOINLINE interpretImpl #-}
 
 reinterpretImpl
   :: (HasCallStack, DispatchOf e ~ Dynamic)
@@ -1229,7 +1244,7 @@
   (`unEff` es) . runSetup . unsafeEff $ \handlerEs -> do
     requireInScopeSetup es handlerEs
     (`unEff` es) $ runHandler (Handler handlerEs handlerImpl) action
-{-# INLINE reinterpretImpl #-}
+{-# NOINLINE reinterpretImpl #-}
 
 interposeImpl
   :: forall e es a. (HasCallStack, DispatchOf e ~ Dynamic, e :> es)
@@ -1253,7 +1268,7 @@
         putEnv es $ Handler newEs handlerImpl
         unEff action es
     )
-{-# INLINE interposeImpl #-}
+{-# NOINLINE interposeImpl #-}
 
 imposeImpl
   :: forall e es handlerEs a b. (HasCallStack, DispatchOf e ~ Dynamic, e :> es)
@@ -1281,7 +1296,7 @@
           putEnv es $ Handler handlerEs handlerImpl
           unEff action es
     )
-{-# INLINE imposeImpl #-}
+{-# NOINLINE imposeImpl #-}
 
 copyRefs
   :: forall es srcEs destEs
