DPDK patches and discussions
 help / color / mirror / Atom feed
From: "Morten Brørup" <mb@smartsharesystems.com>
To: "Andrew Rybchenko" <andrew.rybchenko@oktetlabs.ru>,
	<dev@dpdk.org>, "Bruce Richardson" <bruce.richardson@intel.com>
Subject: RE: [PATCH] mempool: micro optimizations
Date: Tue, 25 Mar 2025 08:13:17 +0100	[thread overview]
Message-ID: <98CBD80474FA8B44BF855DF32C47DC35E9FB68@smartserver.smartshare.dk> (raw)
In-Reply-To: <20250226155923.128859-1-mb@smartsharesystems.com>

PING for review.

@Bruce, you seemed to acknowledge this, but never sent a formal Ack.

Med venlig hilsen / Kind regards,
-Morten Brørup

> -----Original Message-----
> From: Morten Brørup [mailto:mb@smartsharesystems.com]
> Sent: Wednesday, 26 February 2025 16.59
> To: Andrew Rybchenko; dev@dpdk.org
> Cc: Morten Brørup
> Subject: [PATCH] mempool: micro optimizations
> 
> The comparisons lcore_id < RTE_MAX_LCORE and lcore_id != LCORE_ID_ANY
> are
> equivalent, but the latter compiles to fewer bytes of code space.
> Similarly for lcore_id >= RTE_MAX_LCORE and lcore_id == LCORE_ID_ANY.
> 
> The rte_mempool_get_ops() function is also used in the fast path, so
> RTE_VERIFY() was replaced by RTE_ASSERT().
> 
> Compilers implicitly consider comparisons of variable == 0 likely, so
> unlikely() was added to the check for no mempool cache (mp->cache_size
> ==
> 0) in the rte_mempool_default_cache() function.
> 
> The rte_mempool_do_generic_put() function for adding objects to a
> mempool
> was refactored as follows:
> - The comparison for the request itself being too big, which is
> considered
>   unlikely, was moved down and out of the code path where the cache has
>   sufficient room for the added objects, which is considered the most
>   likely code path.
> - Added __rte_assume() about the cache length, size and threshold, for
>   compiler optimization when "n" is compile time constant.
> - Added __rte_assume() about "ret" being zero, so other functions using
>   the value returned by this function can be potentially optimized by
> the
>   compiler; especially when it merges multiple sequential code paths of
>   inlined code depending on the return value being either zero or
>   negative.
> - The refactored source code (with comments) made the separate comment
>   describing the cache flush/add algorithm superfluous, so it was
> removed.
> 
> A few more likely()/unlikely() were added.
> 
> A few comments were improved for readability.
> 
> Some assertions, RTE_ASSERT(), were added. Most importantly to assert
> that
> the return values of the mempool drivers' enqueue and dequeue
> operations
> are API compliant, i.e. 0 (for success) or negative (for failure), and
> never positive.
> 
> Signed-off-by: Morten Brørup <mb@smartsharesystems.com>
> ---
>  lib/mempool/rte_mempool.h | 67 ++++++++++++++++++++++-----------------
>  1 file changed, 38 insertions(+), 29 deletions(-)
> 
> diff --git a/lib/mempool/rte_mempool.h b/lib/mempool/rte_mempool.h
> index c495cc012f..aedc100964 100644
> --- a/lib/mempool/rte_mempool.h
> +++ b/lib/mempool/rte_mempool.h
> @@ -334,7 +334,7 @@ struct __rte_cache_aligned rte_mempool {
>  #ifdef RTE_LIBRTE_MEMPOOL_STATS
>  #define RTE_MEMPOOL_STAT_ADD(mp, name, n) do {
> \
>  		unsigned int __lcore_id = rte_lcore_id();
> \
> -		if (likely(__lcore_id < RTE_MAX_LCORE))
> \
> +		if (likely(__lcore_id != LCORE_ID_ANY))
> \
>  			(mp)->stats[__lcore_id].name += (n);
> \
>  		else
> \
>  			rte_atomic_fetch_add_explicit(&((mp)-
> >stats[RTE_MAX_LCORE].name),  \
> @@ -751,7 +751,7 @@ extern struct rte_mempool_ops_table
> rte_mempool_ops_table;
>  static inline struct rte_mempool_ops *
>  rte_mempool_get_ops(int ops_index)
>  {
> -	RTE_VERIFY((ops_index >= 0) && (ops_index <
> RTE_MEMPOOL_MAX_OPS_IDX));
> +	RTE_ASSERT((ops_index >= 0) && (ops_index <
> RTE_MEMPOOL_MAX_OPS_IDX));
> 
>  	return &rte_mempool_ops_table.ops[ops_index];
>  }
> @@ -791,7 +791,8 @@ rte_mempool_ops_dequeue_bulk(struct rte_mempool
> *mp,
>  	rte_mempool_trace_ops_dequeue_bulk(mp, obj_table, n);
>  	ops = rte_mempool_get_ops(mp->ops_index);
>  	ret = ops->dequeue(mp, obj_table, n);
> -	if (ret == 0) {
> +	RTE_ASSERT(ret <= 0);
> +	if (likely(ret == 0)) {
>  		RTE_MEMPOOL_STAT_ADD(mp, get_common_pool_bulk, 1);
>  		RTE_MEMPOOL_STAT_ADD(mp, get_common_pool_objs, n);
>  	}
> @@ -816,11 +817,14 @@ rte_mempool_ops_dequeue_contig_blocks(struct
> rte_mempool *mp,
>  		void **first_obj_table, unsigned int n)
>  {
>  	struct rte_mempool_ops *ops;
> +	int ret;
> 
>  	ops = rte_mempool_get_ops(mp->ops_index);
>  	RTE_ASSERT(ops->dequeue_contig_blocks != NULL);
>  	rte_mempool_trace_ops_dequeue_contig_blocks(mp, first_obj_table,
> n);
> -	return ops->dequeue_contig_blocks(mp, first_obj_table, n);
> +	ret = ops->dequeue_contig_blocks(mp, first_obj_table, n);
> +	RTE_ASSERT(ret <= 0);
> +	return ret;
>  }
> 
>  /**
> @@ -848,6 +852,7 @@ rte_mempool_ops_enqueue_bulk(struct rte_mempool
> *mp, void * const *obj_table,
>  	rte_mempool_trace_ops_enqueue_bulk(mp, obj_table, n);
>  	ops = rte_mempool_get_ops(mp->ops_index);
>  	ret = ops->enqueue(mp, obj_table, n);
> +	RTE_ASSERT(ret <= 0);
>  #ifdef RTE_LIBRTE_MEMPOOL_DEBUG
>  	if (unlikely(ret < 0))
>  		RTE_MEMPOOL_LOG(CRIT, "cannot enqueue %u objects to mempool
> %s",
> @@ -1333,10 +1338,10 @@ rte_mempool_cache_free(struct rte_mempool_cache
> *cache);
>  static __rte_always_inline struct rte_mempool_cache *
>  rte_mempool_default_cache(struct rte_mempool *mp, unsigned lcore_id)
>  {
> -	if (mp->cache_size == 0)
> +	if (unlikely(mp->cache_size == 0))
>  		return NULL;
> 
> -	if (lcore_id >= RTE_MAX_LCORE)
> +	if (unlikely(lcore_id == LCORE_ID_ANY))
>  		return NULL;
> 
>  	rte_mempool_trace_default_cache(mp, lcore_id,
> @@ -1383,32 +1388,33 @@ rte_mempool_do_generic_put(struct rte_mempool
> *mp, void * const *obj_table,
>  {
>  	void **cache_objs;
> 
> -	/* No cache provided */
> +	/* No cache provided? */
>  	if (unlikely(cache == NULL))
>  		goto driver_enqueue;
> 
> -	/* increment stat now, adding in mempool always success */
> +	/* Increment stats now, adding in mempool always succeeds. */
>  	RTE_MEMPOOL_CACHE_STAT_ADD(cache, put_bulk, 1);
>  	RTE_MEMPOOL_CACHE_STAT_ADD(cache, put_objs, n);
> 
> -	/* The request itself is too big for the cache */
> -	if (unlikely(n > cache->flushthresh))
> -		goto driver_enqueue_stats_incremented;
> -
> -	/*
> -	 * The cache follows the following algorithm:
> -	 *   1. If the objects cannot be added to the cache without
> crossing
> -	 *      the flush threshold, flush the cache to the backend.
> -	 *   2. Add the objects to the cache.
> -	 */
> -
> -	if (cache->len + n <= cache->flushthresh) {
> +	__rte_assume(cache->flushthresh <= RTE_MEMPOOL_CACHE_MAX_SIZE *
> 2);
> +	__rte_assume(cache->len <= RTE_MEMPOOL_CACHE_MAX_SIZE * 2);
> +	__rte_assume(cache->len <= cache->flushthresh);
> +	if (likely(cache->len + n <= cache->flushthresh)) {
> +		/* Sufficient room in the cache for the objects. */
>  		cache_objs = &cache->objs[cache->len];
>  		cache->len += n;
> -	} else {
> +	} else if (n <= cache->flushthresh) {
> +		/*
> +		 * The cache is big enough for the objects, but - as
> detected by
> +		 * the comparison above - has insufficient room for them.
> +		 * Flush the cache to make room for the objects.
> +		 */
>  		cache_objs = &cache->objs[0];
>  		rte_mempool_ops_enqueue_bulk(mp, cache_objs, cache->len);
>  		cache->len = n;
> +	} else {
> +		/* The request itself is too big for the cache. */
> +		goto driver_enqueue_stats_incremented;
>  	}
> 
>  	/* Add the objects to the cache. */
> @@ -1515,7 +1521,7 @@ rte_mempool_do_generic_get(struct rte_mempool
> *mp, void **obj_table,
>  	uint32_t index, len;
>  	void **cache_objs;
> 
> -	/* No cache provided */
> +	/* No cache provided? */
>  	if (unlikely(cache == NULL)) {
>  		remaining = n;
>  		goto driver_dequeue;
> @@ -1524,6 +1530,7 @@ rte_mempool_do_generic_get(struct rte_mempool
> *mp, void **obj_table,
>  	/* The cache is a stack, so copy will be in reverse order. */
>  	cache_objs = &cache->objs[cache->len];
> 
> +	__rte_assume(cache->len <= RTE_MEMPOOL_CACHE_MAX_SIZE * 2);
>  	if (__rte_constant(n) && n <= cache->len) {
>  		/*
>  		 * The request size is known at build time, and
> @@ -1556,7 +1563,7 @@ rte_mempool_do_generic_get(struct rte_mempool
> *mp, void **obj_table,
>  	 * where the entire request can be satisfied from the cache
>  	 * has already been handled above, so omit handling it here.
>  	 */
> -	if (!__rte_constant(n) && remaining == 0) {
> +	if (!__rte_constant(n) && likely(remaining == 0)) {
>  		/* The entire request is satisfied from the cache. */
> 
>  		RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_bulk, 1);
> @@ -1565,7 +1572,7 @@ rte_mempool_do_generic_get(struct rte_mempool
> *mp, void **obj_table,
>  		return 0;
>  	}
> 
> -	/* if dequeue below would overflow mem allocated for cache */
> +	/* Dequeue below would overflow mem allocated for cache? */
>  	if (unlikely(remaining > RTE_MEMPOOL_CACHE_MAX_SIZE))
>  		goto driver_dequeue;
> 
> @@ -1574,8 +1581,7 @@ rte_mempool_do_generic_get(struct rte_mempool
> *mp, void **obj_table,
>  			cache->size + remaining);
>  	if (unlikely(ret < 0)) {
>  		/*
> -		 * We are buffer constrained, and not able to allocate
> -		 * cache + remaining.
> +		 * We are buffer constrained, and not able to fetch all
> that.
>  		 * Do not fill the cache, just satisfy the remaining part
> of
>  		 * the request directly from the backend.
>  		 */
> @@ -1583,6 +1589,8 @@ rte_mempool_do_generic_get(struct rte_mempool
> *mp, void **obj_table,
>  	}
> 
>  	/* Satisfy the remaining part of the request from the filled
> cache. */
> +	__rte_assume(cache->size <= RTE_MEMPOOL_CACHE_MAX_SIZE);
> +	__rte_assume(remaining <= RTE_MEMPOOL_CACHE_MAX_SIZE);
>  	cache_objs = &cache->objs[cache->size + remaining];
>  	for (index = 0; index < remaining; index++)
>  		*obj_table++ = *--cache_objs;
> @@ -1599,7 +1607,7 @@ rte_mempool_do_generic_get(struct rte_mempool
> *mp, void **obj_table,
>  	/* Get remaining objects directly from the backend. */
>  	ret = rte_mempool_ops_dequeue_bulk(mp, obj_table, remaining);
> 
> -	if (ret < 0) {
> +	if (unlikely(ret < 0)) {
>  		if (likely(cache != NULL)) {
>  			cache->len = n - remaining;
>  			/*
> @@ -1619,6 +1627,7 @@ rte_mempool_do_generic_get(struct rte_mempool
> *mp, void **obj_table,
>  			RTE_MEMPOOL_STAT_ADD(mp, get_success_bulk, 1);
>  			RTE_MEMPOOL_STAT_ADD(mp, get_success_objs, n);
>  		}
> +		__rte_assume(ret == 0);
>  	}
> 
>  	return ret;
> @@ -1650,7 +1659,7 @@ rte_mempool_generic_get(struct rte_mempool *mp,
> void **obj_table,
>  {
>  	int ret;
>  	ret = rte_mempool_do_generic_get(mp, obj_table, n, cache);
> -	if (ret == 0)
> +	if (likely(ret == 0))
>  		RTE_MEMPOOL_CHECK_COOKIES(mp, obj_table, n, 1);
>  	rte_mempool_trace_generic_get(mp, obj_table, n, cache);
>  	return ret;
> @@ -1741,7 +1750,7 @@ rte_mempool_get_contig_blocks(struct rte_mempool
> *mp,
>  	int ret;
> 
>  	ret = rte_mempool_ops_dequeue_contig_blocks(mp, first_obj_table,
> n);
> -	if (ret == 0) {
> +	if (likely(ret == 0)) {
>  		RTE_MEMPOOL_STAT_ADD(mp, get_success_bulk, 1);
>  		RTE_MEMPOOL_STAT_ADD(mp, get_success_blks, n);
>  		RTE_MEMPOOL_CONTIG_BLOCKS_CHECK_COOKIES(mp,
> first_obj_table, n,
> --
> 2.43.0


  parent reply	other threads:[~2025-03-25  7:13 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-26 15:59 Morten Brørup
2025-02-26 16:53 ` Bruce Richardson
2025-02-27  9:14   ` Morten Brørup
2025-02-27  9:17     ` Bruce Richardson
2025-02-28 16:59       ` Morten Brørup
2025-03-25  7:13 ` Morten Brørup [this message]
2025-03-27 17:15 ` Bruce Richardson
2025-03-27 19:30   ` Morten Brørup

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=98CBD80474FA8B44BF855DF32C47DC35E9FB68@smartserver.smartshare.dk \
    --to=mb@smartsharesystems.com \
    --cc=andrew.rybchenko@oktetlabs.ru \
    --cc=bruce.richardson@intel.com \
    --cc=dev@dpdk.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).