Commit 6255955d08 for openssl.org

commit 6255955d08636ed533e36d663983c506533e1316
Author: Nikola Pajkovsky <nikolap@openssl.org>
Date:   Tue Nov 4 12:15:52 2025 +0100

    method store performance improvements

    The proposed architectural change focuses on improving concurrency and
    reducing contention within the method store. The fundamental concept
    involves moving away from a monolithic synchronisation
    mechanism—specifically, a single read-write lock (rwlock)—that
    currently guards the entire method store.

    Instead of this single point of contention, the strategy is to
    introduce per-shard synchronisation. This means the method store will
    be partitioned, or sharded, into several independent segments. Each of
    these segments, or shards, will be protected by its own dedicated
    read-write lock.

    The data in the table below was generated by running evp_fetch twenty times per thread.

    |---------+----------+---------+---------+---------+---------+---------+---+--------+--------+--------+--------+--------|
    |                    | Shards (u/sec)                                  |   | Improvements %
    |---------+----------+---------+---------+---------+---------+---------+---+--------+--------+--------+--------+--------|
    | Threads | Base     |       2 |       4 |       8 |      16 |      32 |   |      2 |      4 |      8 |     16 |     32 |
    |---------+----------+---------+---------+---------+---------+---------+---+--------+--------+--------+--------+--------|
    |       1 |  0.18282 | 0.18497 | 0.18306 | 0.18314 | 0.18485 | 0.18352 |   |   1.17 |   0.13 |   0.18 |   1.11 |   0.39 |
    |       2 |  0.43588 | 0.35560 | 0.34131 | 0.32516 | 0.33948 | 0.35076 |   | -18.42 | -21.70 | -25.40 | -22.12 | -19.53 |
    |       4 |  1.58185 | 1.06459 | 1.06258 | 0.98698 | 0.98700 | 1.06689 |   | -32.70 | -32.83 | -37.61 | -37.60 | -32.55 |
    |       8 |  3.15686 | 1.75061 | 1.67458 | 1.50241 | 1.62453 | 1.74750 |   | -44.55 | -46.95 | -52.41 | -48.54 | -44.64 |
    |      16 |  5.53647 | 2.83137 | 2.58007 | 2.65972 | 2.64882 | 2.82755 |   | -48.86 | -53.40 | -51.96 | -52.16 | -48.93 |
    |      32 | 10.72727 | 4.97483 | 4.43692 | 4.52524 | 4.68358 | 4.84840 |   | -53.62 | -58.64 | -57.82 | -56.34 | -54.80 |
    |      64 | 21.12103 | 9.43241 | 7.79981 | 7.91148 | 8.33305 | 8.34230 |   | -55.34 | -63.07 | -62.54 | -60.55 | -60.50 |

    Perf tests were running on the system:
      Architecture: x86_64
      CPU op-mode(s): 32-bit, 64-bit
      Address sizes: 46 bits physical, 48 bits virtual
      Byte Order: Little Endian
      CPU(s): 96
      On-line CPU(s) list: 0-95
      Vendor ID: GenuineIntel
      Model name: Intel(R) Xeon(R) Gold 6248R CPU @ 3.00GHz
      CPU family: 6
      Model: 85
      Thread(s) per core: 2
      Core(s) per socket: 24
      Socket(s): 2

    The most performant option is a configuration with 512 cache entries with
    4 shards. There are two new defines NUM_SHARDS, and CACHE_SIZE which
    can be tweaked at will.

    Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>

    Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
    Reviewed-by: Norbert Pocs <norbertp@openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/29205)

diff --git a/crypto/property/property.c b/crypto/property/property.c
index 4c35d1eb7e..067cfa8a87 100644
--- a/crypto/property/property.c
+++ b/crypto/property/property.c
@@ -12,26 +12,36 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <openssl/crypto.h>
-#include "internal/core.h"
 #include "internal/property.h"
 #include "internal/provider.h"
 #include "internal/tsan_assist.h"
-#include "crypto/ctype.h"
 #include <openssl/lhash.h>
 #include <openssl/rand.h>
 #include <openssl/trace.h>
-#include "internal/thread_once.h"
-#include "crypto/lhash.h"
 #include "crypto/sparse_array.h"
 #include "property_local.h"
 #include "crypto/context.h"

+/*
+ * The shard count was determined through performance testing with the evp_fetch
+ * tool on an Intel Xeon Gold 6248R CPU @ 3.00GHz. Testing showed that 4 shards
+ * combined with CACHE_SIZE delivered the best performance for 16 or
+ * more threads, and close to best performance at below 16 threads.
+ */
+#ifndef NUM_SHARDS
+#define NUM_SHARDS 4
+#endif
+
+#ifndef CACHE_SIZE
+#define CACHE_SIZE 512
+#endif
+
 /*
  * The number of elements in the query cache before we initiate a flush.
  * If reducing this, also ensure the stochastic test in test/property_test.c
  * isn't likely to fail.
  */
-#define IMPL_CACHE_FLUSH_THRESHOLD 500
+#define IMPL_CACHE_FLUSH_THRESHOLD (CACHE_SIZE / NUM_SHARDS)

 typedef struct {
     void *method;
@@ -62,22 +72,15 @@ typedef struct {
     LHASH_OF(QUERY) *cache;
 } ALGORITHM;

-struct ossl_method_store_st {
-    OSSL_LIB_CTX *ctx;
+typedef struct {
     SPARSE_ARRAY_OF(ALGORITHM) * algs;
+
     /*
-     * Lock to protect the |algs| array from concurrent writing, when
-     * individual implementations or queries are inserted.  This is used
+     * Lock to protect each shard of |algs| from concurrent writing,
+     * when individual implementations or queries are inserted.  This is used
      * by the appropriate functions here.
      */
     CRYPTO_RWLOCK *lock;
-    /*
-     * Lock to reserve the whole store.  This is used when fetching a set
-     * of algorithms, via these functions, found in crypto/core_fetch.c:
-     * ossl_method_construct_reserve_store()
-     * ossl_method_construct_unreserve_store()
-     */
-    CRYPTO_RWLOCK *biglock;

     /* query cache specific values */

@@ -86,6 +89,18 @@ struct ossl_method_store_st {

     /* Flag: 1 if query cache entries for all algs need flushing */
     int cache_need_flush;
+} STORED_ALGORITHMS;
+
+struct ossl_method_store_st {
+    OSSL_LIB_CTX *ctx;
+    STORED_ALGORITHMS *algs;
+    /*
+     * Lock to reserve the whole store.  This is used when fetching a set
+     * of algorithms, via these functions, found in crypto/core_fetch.c:
+     * ossl_method_construct_reserve_store()
+     * ossl_method_construct_unreserve_store()
+     */
+    CRYPTO_RWLOCK *biglock;
 };

 typedef struct {
@@ -106,9 +121,11 @@ typedef struct ossl_global_properties_st {
 #endif
 } OSSL_GLOBAL_PROPERTIES;

-static void ossl_method_cache_flush_alg(OSSL_METHOD_STORE *store,
+#define stored_algs_shard(store, nid) (&(store)->algs[(nid) & (NUM_SHARDS - 1)])
+
+static void ossl_method_cache_flush_alg(STORED_ALGORITHMS *sa,
     ALGORITHM *alg);
-static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid);
+static void ossl_method_cache_flush(STORED_ALGORITHMS *sa, int nid);

 /* Global properties are stored per library context */
 void ossl_ctx_global_properties_free(void *vglobp)
@@ -169,17 +186,17 @@ static void ossl_method_free(METHOD *method)
     (*method->free)(method->method);
 }

-static __owur int ossl_property_read_lock(OSSL_METHOD_STORE *p)
+static __owur int ossl_property_read_lock(STORED_ALGORITHMS *p)
 {
     return p != NULL ? CRYPTO_THREAD_read_lock(p->lock) : 0;
 }

-static __owur int ossl_property_write_lock(OSSL_METHOD_STORE *p)
+static __owur int ossl_property_write_lock(STORED_ALGORITHMS *p)
 {
     return p != NULL ? CRYPTO_THREAD_write_lock(p->lock) : 0;
 }

-static int ossl_property_unlock(OSSL_METHOD_STORE *p)
+static int ossl_property_unlock(STORED_ALGORITHMS *p)
 {
     return p != 0 ? CRYPTO_THREAD_unlock(p->lock) : 0;
 }
@@ -224,7 +241,7 @@ static void impl_cache_flush_alg(ossl_uintmax_t idx, ALGORITHM *alg)

 static void alg_cleanup(ossl_uintmax_t idx, ALGORITHM *a, void *arg)
 {
-    OSSL_METHOD_STORE *store = arg;
+    STORED_ALGORITHMS *sa = arg;

     if (a != NULL) {
         sk_IMPLEMENTATION_pop_free(a->impls, &impl_free);
@@ -232,8 +249,48 @@ static void alg_cleanup(ossl_uintmax_t idx, ALGORITHM *a, void *arg)
         lh_QUERY_free(a->cache);
         OPENSSL_free(a);
     }
-    if (store != NULL)
-        ossl_sa_ALGORITHM_set(store->algs, idx, NULL);
+    if (sa != NULL)
+        ossl_sa_ALGORITHM_set(sa->algs, idx, NULL);
+}
+
+static void stored_algs_free(STORED_ALGORITHMS *sa)
+{
+    if (sa == NULL)
+        return;
+
+    for (int i = 0; i < NUM_SHARDS; ++i) {
+        ossl_sa_ALGORITHM_doall_arg(sa[i].algs, &alg_cleanup, &sa[i]);
+        ossl_sa_ALGORITHM_free(sa[i].algs);
+        CRYPTO_THREAD_lock_free(sa[i].lock);
+    }
+
+    OPENSSL_free(sa);
+}
+
+static STORED_ALGORITHMS *stored_algs_new(void)
+{
+    STORED_ALGORITHMS *ret;
+
+    ret = OPENSSL_calloc(NUM_SHARDS, sizeof(STORED_ALGORITHMS));
+    if (ret == NULL)
+        return NULL;
+
+    for (int i = 0; i < NUM_SHARDS; ++i) {
+        ret[i].algs = ossl_sa_ALGORITHM_new();
+        if (ret[i].algs == NULL)
+            goto err;
+
+        ret[i].lock = CRYPTO_THREAD_lock_new();
+        if (ret[i].lock == NULL)
+            goto err;
+    }
+
+    return ret;
+
+err:
+    stored_algs_free(ret);
+
+    return NULL;
 }

 /*
@@ -247,8 +304,7 @@ OSSL_METHOD_STORE *ossl_method_store_new(OSSL_LIB_CTX *ctx)
     res = OPENSSL_zalloc(sizeof(*res));
     if (res != NULL) {
         res->ctx = ctx;
-        if ((res->algs = ossl_sa_ALGORITHM_new()) == NULL
-            || (res->lock = CRYPTO_THREAD_lock_new()) == NULL
+        if ((res->algs = stored_algs_new()) == NULL
             || (res->biglock = CRYPTO_THREAD_lock_new()) == NULL) {
             ossl_method_store_free(res);
             return NULL;
@@ -259,14 +315,12 @@ OSSL_METHOD_STORE *ossl_method_store_new(OSSL_LIB_CTX *ctx)

 void ossl_method_store_free(OSSL_METHOD_STORE *store)
 {
-    if (store != NULL) {
-        if (store->algs != NULL)
-            ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_cleanup, store);
-        ossl_sa_ALGORITHM_free(store->algs);
-        CRYPTO_THREAD_lock_free(store->lock);
-        CRYPTO_THREAD_lock_free(store->biglock);
-        OPENSSL_free(store);
-    }
+    if (store == NULL)
+        return;
+
+    stored_algs_free(store->algs);
+    CRYPTO_THREAD_lock_free(store->biglock);
+    OPENSSL_free(store);
 }

 int ossl_method_lock_store(OSSL_METHOD_STORE *store)
@@ -279,14 +333,14 @@ int ossl_method_unlock_store(OSSL_METHOD_STORE *store)
     return store != NULL ? CRYPTO_THREAD_unlock(store->biglock) : 0;
 }

-static ALGORITHM *ossl_method_store_retrieve(OSSL_METHOD_STORE *store, int nid)
+static ALGORITHM *ossl_method_store_retrieve(STORED_ALGORITHMS *sa, int nid)
 {
-    return ossl_sa_ALGORITHM_get(store->algs, nid);
+    return ossl_sa_ALGORITHM_get(sa->algs, nid);
 }

-static int ossl_method_store_insert(OSSL_METHOD_STORE *store, ALGORITHM *alg)
+static int ossl_method_store_insert(STORED_ALGORITHMS *sa, ALGORITHM *alg)
 {
-    return ossl_sa_ALGORITHM_set(store->algs, alg->nid, alg);
+    return ossl_sa_ALGORITHM_set(sa->algs, alg->nid, alg);
 }

 /**
@@ -319,6 +373,7 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
     int (*method_up_ref)(void *),
     void (*method_destruct)(void *))
 {
+    STORED_ALGORITHMS *sa;
     ALGORITHM *alg = NULL;
     IMPLEMENTATION *impl;
     int ret = 0;
@@ -346,8 +401,10 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
     }
     impl->provider = prov;

+    sa = stored_algs_shard(store, nid);
+
     /* Insert into the hash table if required */
-    if (!ossl_property_write_lock(store)) {
+    if (!ossl_property_write_lock(sa)) {
         impl_free(impl);
         return 0;
     }
@@ -363,7 +420,7 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
      * method to the algorithm cache, in case the one selected by the next
      * query selects a different implementation
      */
-    ossl_method_cache_flush(store, nid);
+    ossl_method_cache_flush(sa, nid);

     /*
      * Parse the properties associated with this method, and convert it to a
@@ -385,16 +442,17 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
      * Check if we have an algorithm cache already for this nid.  If so use
      * it, otherwise, create it, and insert it into the store
      */
-    alg = ossl_method_store_retrieve(store, nid);
+    alg = ossl_method_store_retrieve(sa, nid);
     if (alg == NULL) {
         if ((alg = OPENSSL_zalloc(sizeof(*alg))) == NULL
             || (alg->impls = sk_IMPLEMENTATION_new_null()) == NULL
             || (alg->cache = lh_QUERY_new(&query_hash, &query_cmp)) == NULL)
             goto err;
         alg->nid = nid;
-        if (!ossl_method_store_insert(store, alg))
+        if (!ossl_method_store_insert(sa, alg))
             goto err;
-        OSSL_TRACE2(QUERY, "Inserted an alg with nid %d into the store %p\n", nid, (void *)store);
+        OSSL_TRACE2(QUERY, "Inserted an alg with nid %d into the stored algorithms %p\n",
+            nid, (void *)sa);
     }

     /* Push onto stack if there isn't one there already */
@@ -420,13 +478,13 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
         OSSL_TRACE_END(QUERY);
 #endif
     }
-    ossl_property_unlock(store);
+    ossl_property_unlock(sa);
     if (ret == 0)
         impl_free(impl);
     return ret;

 err:
-    ossl_property_unlock(store);
+    ossl_property_unlock(sa);
     alg_cleanup(0, alg, NULL);
     impl_free(impl);
     return 0;
@@ -436,17 +494,19 @@ int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid,
     const void *method)
 {
     ALGORITHM *alg = NULL;
+    STORED_ALGORITHMS *sa;
     int i;

     if (nid <= 0 || method == NULL || store == NULL)
         return 0;

-    if (!ossl_property_write_lock(store))
+    sa = stored_algs_shard(store, nid);
+    if (!ossl_property_write_lock(sa))
         return 0;
-    ossl_method_cache_flush(store, nid);
-    alg = ossl_method_store_retrieve(store, nid);
+    ossl_method_cache_flush(sa, nid);
+    alg = ossl_method_store_retrieve(sa, nid);
     if (alg == NULL) {
-        ossl_property_unlock(store);
+        ossl_property_unlock(sa);
         return 0;
     }

@@ -461,16 +521,16 @@ int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid,
         if (impl->method.method == method) {
             impl_free(impl);
             (void)sk_IMPLEMENTATION_delete(alg->impls, i);
-            ossl_property_unlock(store);
+            ossl_property_unlock(sa);
             return 1;
         }
     }
-    ossl_property_unlock(store);
+    ossl_property_unlock(sa);
     return 0;
 }

 struct alg_cleanup_by_provider_data_st {
-    OSSL_METHOD_STORE *store;
+    STORED_ALGORITHMS *sa;
     const OSSL_PROVIDER *prov;
 };

@@ -532,7 +592,7 @@ alg_cleanup_by_provider(ossl_uintmax_t idx, ALGORITHM *alg, void *arg)
      * any implementation, though.
      */
     if (count > 0)
-        ossl_method_cache_flush_alg(data->store, alg);
+        ossl_method_cache_flush_alg(data->sa, alg);
 }

 int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store,
@@ -540,12 +600,16 @@ int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store,
 {
     struct alg_cleanup_by_provider_data_st data;

-    if (!ossl_property_write_lock(store))
-        return 0;
-    data.prov = prov;
-    data.store = store;
-    ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_cleanup_by_provider, &data);
-    ossl_property_unlock(store);
+    for (int k = 0; k < NUM_SHARDS; ++k) {
+        STORED_ALGORITHMS *sa = &store->algs[k];
+
+        if (!ossl_property_write_lock(sa))
+            return 0;
+        data.prov = prov;
+        data.sa = sa;
+        ossl_sa_ALGORITHM_doall_arg(sa->algs, &alg_cleanup_by_provider, &data);
+        ossl_property_unlock(sa);
+    }
     return 1;
 }

@@ -584,20 +648,24 @@ void ossl_method_store_do_all(OSSL_METHOD_STORE *store,
     STACK_OF(ALGORITHM) *tmpalgs;
     ALGORITHM *alg;

-    if (store != NULL) {
+    if (store == NULL)
+        return;
+
+    for (int k = 0; k < NUM_SHARDS; ++k) {
+        STORED_ALGORITHMS *sa = &store->algs[k];

-        if (!ossl_property_read_lock(store))
+        if (!ossl_property_read_lock(sa))
             return;

         tmpalgs = sk_ALGORITHM_new_reserve(NULL,
-            (int)ossl_sa_ALGORITHM_num(store->algs));
+            (int)ossl_sa_ALGORITHM_num(sa->algs));
         if (tmpalgs == NULL) {
-            ossl_property_unlock(store);
+            ossl_property_unlock(sa);
             return;
         }

-        ossl_sa_ALGORITHM_doall_arg(store->algs, alg_copy, tmpalgs);
-        ossl_property_unlock(store);
+        ossl_sa_ALGORITHM_doall_arg(sa->algs, alg_copy, tmpalgs);
+        ossl_property_unlock(sa);
         numalgs = sk_ALGORITHM_num(tmpalgs);
         for (i = 0; i < numalgs; i++) {
             alg = sk_ALGORITHM_value(tmpalgs, i);
@@ -643,6 +711,7 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
     const OSSL_PROVIDER *prov = prov_rw != NULL ? *prov_rw : NULL;
     int ret = 0;
     int j, best = -1, score, optional;
+    STORED_ALGORITHMS *sa;

     if (nid <= 0 || method == NULL || store == NULL)
         return 0;
@@ -653,18 +722,23 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
         return 0;
 #endif

+    sa = stored_algs_shard(store, nid);
+
     /* This only needs to be a read lock, because the query won't create anything */
-    if (!ossl_property_read_lock(store))
+    if (!ossl_property_read_lock(sa))
         return 0;

-    OSSL_TRACE2(QUERY, "Retrieving by nid %d from store %p\n", nid, (void *)store);
-    alg = ossl_method_store_retrieve(store, nid);
+    OSSL_TRACE2(QUERY, "Retrieving by nid %d from stored algorithms %p\n",
+        nid, (void *)sa);
+    alg = ossl_method_store_retrieve(sa, nid);
     if (alg == NULL) {
-        ossl_property_unlock(store);
-        OSSL_TRACE2(QUERY, "Failed to retrieve by nid %d from store %p\n", nid, (void *)store);
+        ossl_property_unlock(sa);
+        OSSL_TRACE2(QUERY, "Failed to retrieve by nid %d from stored algorithms %p\n",
+            nid, (void *)sa);
         return 0;
     }
-    OSSL_TRACE2(QUERY, "Retrieved by nid %d from store %p\n", nid, (void *)store);
+    OSSL_TRACE2(QUERY, "Retrieved by nid %d from stored algorithms %p\n",
+        nid, (void *)sa);

     /*
      * If a property query string is provided, convert it to an
@@ -699,7 +773,8 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
      */
     if (pq == NULL) {
         for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
-            if ((impl = sk_IMPLEMENTATION_value(alg->impls, j)) != NULL
+            impl = sk_IMPLEMENTATION_value(alg->impls, j);
+            if (impl != NULL
                 && (prov == NULL || impl->provider == prov)) {
                 best_impl = impl;
                 ret = 1;
@@ -716,7 +791,8 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
      */
     optional = ossl_property_has_optional(pq);
     for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
-        if ((impl = sk_IMPLEMENTATION_value(alg->impls, j)) != NULL
+        impl = sk_IMPLEMENTATION_value(alg->impls, j);
+        if (impl != NULL
             && (prov == NULL || impl->provider == prov)) {
             score = ossl_property_match_count(pq, impl->properties);
             if (score > best) {
@@ -752,33 +828,38 @@ fin:
     OSSL_TRACE_END(QUERY);
 #endif

-    ossl_property_unlock(store);
+    ossl_property_unlock(sa);
     ossl_property_free(p2);
     return ret;
 }

-static void ossl_method_cache_flush_alg(OSSL_METHOD_STORE *store,
+static void ossl_method_cache_flush_alg(STORED_ALGORITHMS *sa,
     ALGORITHM *alg)
 {
-    store->cache_nelem -= lh_QUERY_num_items(alg->cache);
+    sa->cache_nelem -= lh_QUERY_num_items(alg->cache);
     impl_cache_flush_alg(0, alg);
 }

-static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid)
+static void ossl_method_cache_flush(STORED_ALGORITHMS *sa, int nid)
 {
-    ALGORITHM *alg = ossl_method_store_retrieve(store, nid);
+    ALGORITHM *alg = ossl_method_store_retrieve(sa, nid);

     if (alg != NULL)
-        ossl_method_cache_flush_alg(store, alg);
+        ossl_method_cache_flush_alg(sa, alg);
 }

 int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store)
 {
-    if (!ossl_property_write_lock(store))
-        return 0;
-    ossl_sa_ALGORITHM_doall(store->algs, &impl_cache_flush_alg);
-    store->cache_nelem = 0;
-    ossl_property_unlock(store);
+    for (int i = 0; i < NUM_SHARDS; ++i) {
+        STORED_ALGORITHMS *sa = &store->algs[i];
+
+        if (!ossl_property_write_lock(sa))
+            return 0;
+        ossl_sa_ALGORITHM_doall(sa->algs, &impl_cache_flush_alg);
+        sa->cache_nelem = 0;
+        ossl_property_unlock(sa);
+    }
+
     return 1;
 }

@@ -836,7 +917,7 @@ static void impl_cache_flush_one_alg(ossl_uintmax_t idx, ALGORITHM *alg,
     lh_QUERY_set_down_load(alg->cache, orig_down_load);
 }

-static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store)
+static void ossl_method_cache_flush_some(STORED_ALGORITHMS *sa)
 {
     IMPL_CACHE_FLUSH state;
     static TSAN_QUALIFIER uint32_t global_seed = 1;
@@ -848,9 +929,11 @@ static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store)
         state.using_global_seed = 1;
         state.seed = tsan_load(&global_seed);
     }
-    store->cache_need_flush = 0;
-    ossl_sa_ALGORITHM_doall_arg(store->algs, &impl_cache_flush_one_alg, &state);
-    store->cache_nelem = state.nelem;
+
+    sa->cache_need_flush = 0;
+    ossl_sa_ALGORITHM_doall_arg(sa->algs, &impl_cache_flush_one_alg, &state);
+    sa->cache_nelem = state.nelem;
+
     /* Without a timer, update the global seed */
     if (state.using_global_seed)
         tsan_add(&global_seed, state.seed);
@@ -862,13 +945,15 @@ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
     ALGORITHM *alg;
     QUERY elem, *r;
     int res = 0;
+    STORED_ALGORITHMS *sa;

     if (nid <= 0 || store == NULL || prop_query == NULL)
         return 0;

-    if (!ossl_property_read_lock(store))
+    sa = stored_algs_shard(store, nid);
+    if (!ossl_property_read_lock(sa))
         return 0;
-    alg = ossl_method_store_retrieve(store, nid);
+    alg = ossl_method_store_retrieve(sa, nid);
     if (alg == NULL)
         goto err;

@@ -882,7 +967,7 @@ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
         res = 1;
     }
 err:
-    ossl_property_unlock(store);
+    ossl_property_unlock(sa);
     return res;
 }

@@ -893,6 +978,7 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
 {
     QUERY elem, *old, *p = NULL;
     ALGORITHM *alg;
+    STORED_ALGORITHMS *sa;
     size_t len;
     int res = 1;

@@ -902,11 +988,12 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
     if (!ossl_assert(prov != NULL))
         return 0;

-    if (!ossl_property_write_lock(store))
+    sa = stored_algs_shard(store, nid);
+    if (!ossl_property_write_lock(sa))
         return 0;
-    if (store->cache_need_flush)
-        ossl_method_cache_flush_some(store);
-    alg = ossl_method_store_retrieve(store, nid);
+    if (sa->cache_need_flush)
+        ossl_method_cache_flush_some(sa);
+    alg = ossl_method_store_retrieve(sa, nid);
     if (alg == NULL)
         goto err;

@@ -915,7 +1002,7 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
         elem.provider = prov;
         if ((old = lh_QUERY_delete(alg->cache, &elem)) != NULL) {
             impl_cache_free(old);
-            store->cache_nelem--;
+            sa->cache_nelem--;
         }
         goto end;
     }
@@ -934,8 +1021,8 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
             goto end;
         }
         if (!lh_QUERY_error(alg->cache)) {
-            if (++store->cache_nelem >= IMPL_CACHE_FLUSH_THRESHOLD)
-                store->cache_need_flush = 1;
+            if (++sa->cache_nelem >= IMPL_CACHE_FLUSH_THRESHOLD)
+                sa->cache_need_flush = 1;
             goto end;
         }
         ossl_method_free(&p->method);
@@ -944,6 +1031,6 @@ err:
     res = 0;
     OPENSSL_free(p);
 end:
-    ossl_property_unlock(store);
+    ossl_property_unlock(sa);
     return res;
 }