RE: [PATCH v9] graph: add optional profiling stats

Morten Brørup <[email protected]>
Newsgroups org.dpdk.dev
Message-ID <[email protected]>
PING for review.

(And obviously, the release notes needs to be for 26.11, not 26.07.)

> -----Original Message-----
> From: Morten Brørup [mailto:[email protected]]
> Sent: Friday, 3 July 2026 17.44
> 
> graph: add optional profiling stats
> 
> Added graph node profiling stats, build time configurable by enabling
> RTE_GRAPH_PROFILE in rte_config.h.
> 
> Signed-off-by: Morten Brørup <[email protected]>
> ---
> v9:
> * Fixed comment still mentioning 32 objects.
> * Moved sample size array outside loop. (AI)
> * Added release note. (AI)
> v8:
> * Added static const array as local variable, instead of indexing
> directly
>   into const array. (AI)
>   This also eliminates the space required between "} [idx];" weirdness.
> * Added build time configurable RTE_GRAPH_PROFILE_BURST_SIZE to replace
>   the hardcoded burst size of 32. (AI)
> v7:
> * Use RTE_DIM() in histogram for loop.
> * Added static_assert for histogram index values.
> * Minor details to please checkpatch.
>   Although I disagree with requiring a space when indexing into
>   a constant array "(const type []){values} [idx];",
>   I have changed the code to comply.
> v6:
> * Consolidate the four histogram entries into one array. (Saeed
> Bishara)
> * Sample at 32 objs instead of a half burst. (Saeed Bishara)
> * Moved stats to different location in rte_node structure. (Jerin)
> * Minor details to please checkpatch.
> v5:
> * Added stats for a half burst and a full burst.
> v4:
> * Added documentation. (AI)
> * Added more comments. (AI)
> * Improved dump. (AI)
> * Debug shows both cycles/call and cycles/obj.
> v3:
> * Debug shows cycles/obj instead of cycles/call.
> * Fixed missing --in-reply-to.
> v2:
> * Fixed indentation.
> ---
>  config/rte_config.h                    |  2 ++
>  doc/guides/prog_guide/graph_lib.rst    |  3 ++
>  doc/guides/rel_notes/release_26_07.rst |  7 ++++
>  lib/graph/graph_debug.c                | 50 ++++++++++++++++++++++++++
>  lib/graph/node.c                       |  2 ++
>  lib/graph/rte_graph_worker_common.h    | 32 +++++++++++++++--
>  6 files changed, 93 insertions(+), 3 deletions(-)
> 
> diff --git a/config/rte_config.h b/config/rte_config.h
> index 0447cdf2ad..7703a6325b 100644
> --- a/config/rte_config.h
> +++ b/config/rte_config.h
> @@ -106,6 +106,8 @@
>  /* rte_graph defines */
>  #define RTE_GRAPH_BURST_SIZE 256
>  #define RTE_LIBRTE_GRAPH_STATS 1
> +/* RTE_GRAPH_PROFILE is not set */
> +#define RTE_GRAPH_PROFILE_BURST_SIZE 32
> 
>  /****** driver defines ********/
> 
> diff --git a/doc/guides/prog_guide/graph_lib.rst
> b/doc/guides/prog_guide/graph_lib.rst
> index 8dd49c19d2..4311c37cc2 100644
> --- a/doc/guides/prog_guide/graph_lib.rst
> +++ b/doc/guides/prog_guide/graph_lib.rst
> @@ -49,6 +49,9 @@ Performance tuning parameters
>    RTE_GRAPH_BURST_SIZE config option.
>    The testing shows, on x86 and arm64 servers, The sweet spot is 256
> burst
>    size. While on arm64 embedded SoCs, it is either 64 or 128.
> +- Enable the ``RTE_GRAPH_PROFILE`` config option for more profiling
> details.
> +  Set the ``RTE_GRAPH_PROFILE_BURST_SIZE`` config option to sample a
> specific
> +  burst size.
>  - Disable node statistics (using ``RTE_LIBRTE_GRAPH_STATS`` config
> option)
>    if not needed.
> 
> diff --git a/doc/guides/rel_notes/release_26_07.rst
> b/doc/guides/rel_notes/release_26_07.rst
> index 8b1bdada1a..cb7d48eeac 100644
> --- a/doc/guides/rel_notes/release_26_07.rst
> +++ b/doc/guides/rel_notes/release_26_07.rst
> @@ -83,6 +83,13 @@ New Features
>    * The size of the ``struct rte_mempool_cache`` was kept
>      for API/ABI compatibility purposes.
> 
> +* **Added optional graph profiling statistics.**
> +
> +  Added build-time configurable graph node profiling statistics via
> +  ``RTE_GRAPH_PROFILE`` in ``rte_config.h``. When enabled, tracks
> cycles
> +  spent processing bursts of 0, 1, ``RTE_GRAPH_PROFILE_BURST_SIZE``,
> +  and ``RTE_GRAPH_BURST_SIZE`` objects per node.
> +
>  * **Added RISC-V vector paths.**
> 
>    * Increased the default SIMD bitwidth to allow using the vector
> extension.
> diff --git a/lib/graph/graph_debug.c b/lib/graph/graph_debug.c
> index e3b8cccdc1..b999fa4140 100644
> --- a/lib/graph/graph_debug.c
> +++ b/lib/graph/graph_debug.c
> @@ -93,6 +93,56 @@ rte_graph_obj_dump(FILE *f, struct rte_graph *g,
> bool all)
>  				n->dispatch.total_sched_fail);
>  		}
>  		fprintf(f, "       total_calls=%" PRId64 "\n", n-
> >total_calls);
> +		if (rte_graph_has_stats_feature())
> +			fprintf(f, "       total_cycles=%" PRIu64 ", avg
> cycles/call=%.1f\n",
> +					n->total_cycles,
> +					n->total_calls == 0 ? 0.0 :
> +					(double)n->total_cycles / (double)n-
> >total_calls);
> +#ifdef RTE_GRAPH_PROFILE
> +		static const uint16_t profile_sample_sizes[] = {
> +				0, 1, RTE_GRAPH_PROFILE_BURST_SIZE,
> RTE_GRAPH_BURST_SIZE};
> +		static_assert(RTE_DIM(profile_sample_sizes) == RTE_DIM(n-
> >usage_stats),
> +				"usage_stats array size mismatch");
> +		int64_t calls_other = n->total_calls;
> +		int64_t cycles_other = n->total_cycles;
> +		int64_t objs_other = n->total_objs;
> +		for (int idx = 0; idx < RTE_DIM(n->usage_stats) + 1; idx++)
> {
> +			uint64_t calls;
> +			uint64_t cycles;
> +			double objs_per_call;
> +			if (idx < RTE_DIM(n->usage_stats)) {
> +				uint16_t idx_objs = profile_sample_sizes[idx];
> +				fprintf(f, "       objs[%u]\n", idx_objs);
> +				calls = n->usage_stats[idx].calls;
> +				cycles = n->usage_stats[idx].cycles;
> +				objs_per_call = (double)idx_objs;
> +				calls_other -= calls;
> +				cycles_other -= cycles;
> +				objs_other -= idx_objs * calls;
> +			} else {
> +				fprintf(f, "       objs[other]\n");
> +				if (calls_other > 0 && cycles_other > 0 &&
> objs_other > 0) {
> +					calls = calls_other;
> +					cycles = cycles_other;
> +					objs_per_call = (double)objs_other /
> (double)calls_other;
> +					fprintf(f, "         avg
> objs/call=%.1f\n", objs_per_call);
> +				} else {
> +					calls = 0;
> +					cycles = 0;
> +					objs_per_call = 0.0;
> +				}
> +			}
> +			fprintf(f, "         calls=%" PRIu64, calls);
> +			if (calls != 0)
> +				fprintf(f, ", cycles=%" PRIu64 ", avg
> cycles/call=%.1f",
> +						cycles,
> +						(double)cycles / (double)calls);
> +			if (calls != 0 && objs_per_call != 0.0)
> +				fprintf(f, ", avg cycles/obj=%.1f",
> +						(double)cycles / (double)calls /
> objs_per_call);
> +			fprintf(f, "\n");
> +		}
> +#endif
>  		for (i = 0; i < n->nb_edges; i++)
>  			fprintf(f, "          edge[%d] <%s>\n", i,
>  				n->nodes[i]->name);
> diff --git a/lib/graph/node.c b/lib/graph/node.c
> index 1fce3e6632..19b38881ae 100644
> --- a/lib/graph/node.c
> +++ b/lib/graph/node.c
> @@ -110,10 +110,12 @@ __rte_node_register(const struct
> rte_node_register *reg)
>  	rte_edge_t i;
>  	size_t sz;
> 
> +#ifndef RTE_GRAPH_PROFILE
>  	/* Limit Node specific metadata to one cacheline on 64B CL
> machine */
>  	RTE_BUILD_BUG_ON((offsetof(struct rte_node, nodes) -
>  			  offsetof(struct rte_node, ctx)) !=
>  			 RTE_CACHE_LINE_MIN_SIZE);
> +#endif
> 
>  	graph_spinlock_lock();
> 
> diff --git a/lib/graph/rte_graph_worker_common.h
> b/lib/graph/rte_graph_worker_common.h
> index 4ab53a533e..00e8f5859d 100644
> --- a/lib/graph/rte_graph_worker_common.h
> +++ b/lib/graph/rte_graph_worker_common.h
> @@ -121,6 +121,17 @@ struct __rte_cache_aligned rte_node {
>  	rte_graph_off_t xstat_off; /**< Offset to xstat counters. */
> 
>  	/** Fast path area cache line 2. */
> +#ifdef RTE_GRAPH_PROFILE
> +	/**
> +	 * Usage when this node processed 0, 1,
> RTE_GRAPH_PROFILE_BURST_SIZE,
> +	 * or RTE_GRAPH_BURST_SIZE objects.
> +	 */
> +	struct __rte_cache_aligned {
> +		uint64_t calls;     /**< Calls done. */
> +		uint64_t cycles;    /**< Cycles spent. */
> +	} usage_stats[4];
> +	/** Fast path area cache line 3. */
> +#endif
>  	__extension__ struct __rte_cache_aligned {
>  #define RTE_NODE_CTX_SZ 16
>  		union {
> @@ -148,8 +159,10 @@ struct __rte_cache_aligned rte_node {
>  	};
>  };
> 
> +#ifndef RTE_GRAPH_PROFILE
>  static_assert(offsetof(struct rte_node, nodes) - offsetof(struct
> rte_node, ctx)
>  	== RTE_CACHE_LINE_MIN_SIZE, "rte_node fast path area must fit in
> 64 bytes");
> +#endif
> 
>  /**
>   * @internal
> @@ -197,7 +210,7 @@ void __rte_node_stream_alloc_size(struct rte_graph
> *graph,
>  static __rte_always_inline void
>  __rte_node_process(struct rte_graph *graph, struct rte_node *node)
>  {
> -	uint64_t start;
> +	uint64_t cycles;
>  	uint16_t rc;
>  	void **objs;
> 
> @@ -206,11 +219,24 @@ __rte_node_process(struct rte_graph *graph,
> struct rte_node *node)
>  	rte_prefetch0(objs);
> 
>  	if (rte_graph_has_stats_feature()) {
> -		start = rte_rdtsc();
> +		cycles = -rte_rdtsc();
>  		rc = node->process(graph, node, objs, node->idx);
> -		node->total_cycles += rte_rdtsc() - start;
> +		cycles += rte_rdtsc();
> +		node->total_cycles += cycles;
>  		node->total_calls++;
>  		node->total_objs += rc;
> +#ifdef RTE_GRAPH_PROFILE
> +		if (rc <= 1) {
> +			node->usage_stats[rc].calls++;
> +			node->usage_stats[rc].cycles += cycles;
> +		} else if (rc == RTE_GRAPH_PROFILE_BURST_SIZE) {
> +			node->usage_stats[2].calls++;
> +			node->usage_stats[2].cycles += cycles;
> +		} else if (rc == RTE_GRAPH_BURST_SIZE) {
> +			node->usage_stats[3].calls++;
> +			node->usage_stats[3].cycles += cycles;
> +		}
> +#endif
>  	} else {
>  		node->process(graph, node, objs, node->idx);
>  	}
> --
> 2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.