From eb0b385e6b4e43ec0643ab4ed4d4f1e17a9ab365 Mon Sep 17 00:00:00 2001
From: Lukas Fittl <lukas@fittl.com>
Date: Tue, 7 Apr 2026 12:32:36 -0700
Subject: [PATCH v16 02/10] instrumentation: Move ExecProcNodeInstr to allow
 inlining

This moves the implementation of ExecProcNodeInstr, the ExecProcNode
variant that gets used when instrumentation is on, to be defined in
instrument.c instead of execProcNode.c, and marks functions it uses
as inline.

This allows compilers to generate an optimized implementation, and
shows a 2 to 5% reduction in instrumentation overhead for queries
that move lots of rows.

Author: Lukas Fittl <lukas@fittl.com>
Suggested-by: Andres Freund <andres@anarazel.de>
Reviewed-by:
Discussion: https://www.postgresql.org/message-id/flat/CAP53PkzdBK8VJ1fS4AZ481LgMN8f9mJiC39ZRHqkFUSYq6KWmg@mail.gmail.com
---
 src/backend/executor/execProcnode.c | 20 --------------------
 src/backend/executor/instrument.c   | 27 ++++++++++++++++++++++++---
 src/include/executor/instrument.h   |  4 ++++
 3 files changed, 28 insertions(+), 23 deletions(-)

diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 132fe37ef60..7c4c66e323f 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -121,7 +121,6 @@
 #include "nodes/nodeFuncs.h"
 
 static TupleTableSlot *ExecProcNodeFirst(PlanState *node);
-static TupleTableSlot *ExecProcNodeInstr(PlanState *node);
 static bool ExecShutdownNode_walker(PlanState *node, void *context);
 
 
@@ -471,25 +470,6 @@ ExecProcNodeFirst(PlanState *node)
 }
 
 
-/*
- * ExecProcNode wrapper that performs instrumentation calls.  By keeping
- * this a separate function, we avoid overhead in the normal case where
- * no instrumentation is wanted.
- */
-static TupleTableSlot *
-ExecProcNodeInstr(PlanState *node)
-{
-	TupleTableSlot *result;
-
-	InstrStartNode(node->instrument);
-
-	result = node->ExecProcNodeReal(node);
-
-	InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);
-
-	return result;
-}
-
 
 /* ----------------------------------------------------------------
  *		MultiExecProcNode
diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c
index 4c3aec7fdee..dd08fc99fb2 100644
--- a/src/backend/executor/instrument.c
+++ b/src/backend/executor/instrument.c
@@ -16,6 +16,8 @@
 #include <unistd.h>
 
 #include "executor/instrument.h"
+#include "executor/tuptable.h"
+#include "nodes/execnodes.h"
 #include "portability/instr_time.h"
 #include "utils/guc_hooks.h"
 
@@ -46,7 +48,7 @@ InstrInitOptions(Instrumentation *instr, int instrument_options)
 	instr->need_timer = (instrument_options & INSTRUMENT_TIMER) != 0;
 }
 
-void
+inline void
 InstrStart(Instrumentation *instr)
 {
 	if (instr->need_timer)
@@ -125,14 +127,14 @@ InstrInitNode(NodeInstrumentation *instr, int instrument_options, bool async_mod
 }
 
 /* Entry to a plan node */
-void
+inline void
 InstrStartNode(NodeInstrumentation *instr)
 {
 	InstrStart(&instr->instr);
 }
 
 /* Exit from a plan node */
-void
+inline void
 InstrStopNode(NodeInstrumentation *instr, double nTuples)
 {
 	double		save_tuplecount = instr->tuplecount;
@@ -166,6 +168,25 @@ InstrStopNode(NodeInstrumentation *instr, double nTuples)
 	}
 }
 
+/*
+ * ExecProcNode wrapper that performs instrumentation calls.  By keeping
+ * this a separate function, we avoid overhead in the normal case where
+ * no instrumentation is wanted.
+ */
+TupleTableSlot *
+ExecProcNodeInstr(PlanState *node)
+{
+	TupleTableSlot *result;
+
+	InstrStartNode(node->instrument);
+
+	result = node->ExecProcNodeReal(node);
+
+	InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);
+
+	return result;
+}
+
 /* Update tuple count */
 void
 InstrUpdateTupleCount(NodeInstrumentation *instr, double nTuples)
diff --git a/src/include/executor/instrument.h b/src/include/executor/instrument.h
index cc9fbb0e2f0..84ef0ad089c 100644
--- a/src/include/executor/instrument.h
+++ b/src/include/executor/instrument.h
@@ -142,6 +142,10 @@ extern void InstrUpdateTupleCount(NodeInstrumentation *instr, double nTuples);
 extern void InstrEndLoop(NodeInstrumentation *instr);
 extern void InstrAggNode(NodeInstrumentation *dst, NodeInstrumentation *add);
 
+typedef struct TupleTableSlot TupleTableSlot;
+typedef struct PlanState PlanState;
+extern TupleTableSlot *ExecProcNodeInstr(PlanState *node);
+
 extern TriggerInstrumentation *InstrAllocTrigger(int n, int instrument_options);
 extern void InstrStartTrigger(TriggerInstrumentation *tginstr);
 extern void InstrStopTrigger(TriggerInstrumentation *tginstr, int64 firings);
-- 
2.47.1

