From: Yury Norov <ynorov@nvidia.com>
To: "Eliot Courtney" <ecourtney@nvidia.com>,
"Greg KH" <gregkh@linuxfoundation.org>,
"Burak Emir" <bqe@google.com>,
"John Hubbard" <jhubbard@nvidia.com>,
"Alice Ryhl" <aliceryhl@google.com>,
"Liam R . Howlett" <liam@infradead.org>,
"Andrew Ballance" <andrewjballance@gmail.com>,
"Matthew Wilcox" <willy@infradead.org>,
"Gary Guo" <gary@garyguo.net>, "Onur Özkan" <work@onurozkan.dev>,
"Pedro Falcato" <pfalcato@suse.de>
Cc: "Alexandre Courbot" <acourbot@nvidia.com>,
"Alistair Popple" <apopple@nvidia.com>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Benno Lossin" <lossin@kernel.org>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Boqun Feng" <boqun@kernel.org>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Danilo Krummrich" <dakr@kernel.org>,
"David Airlie" <airlied@gmail.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Rasmus Villemoes" <linux@rasmusvillemoes.dk>,
"Simona Vetter" <simona@ffwll.ch>,
"Tamir Duberstein" <tamird@kernel.org>,
"Timur Tabi" <ttabi@nvidia.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Yury Norov" <yury.norov@gmail.com>, "Zhi Wang" <zhiw@nvidia.com>,
maple-tree@lists.infradead.org, linux-kernel@vger.kernel.org,
linux-mm@kvack.org, linux-fsdevel@vger.kernel.org,
nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org,
rust-for-linux@vger.kernel.org
Subject: Re: [PATCH v3] lib: test bitmap vs IDA vs Maple Tree performance for region allocations
Date: Wed, 22 Jul 2026 11:04:36 -0400 [thread overview]
Message-ID: <amDcBOmcxxz0bCOF@yury> (raw)
In-Reply-To: <20260717053241.916441-1-ynorov@nvidia.com>
Guys, please send your comments / tags!
If no objections, I'll move it in bitmap-for-next before the end of week.
Thanks,
Yury
On Fri, Jul 17, 2026 at 01:32:40AM -0400, Yury Norov wrote:
> Compare the cost of allocating and freeing variable-sized regions using
> a bitmap, IDA and a Maple Tree. All implementations process the same
> randomly generated sequence of regions containing up to 32 entries, until
> the configured capacity is exhausted.
>
> The benchmark exercises monotonic allocation into an initially empty pool,
> followed by reverse-order freeing. It does not model fragmentation or
> interleaved allocation and freeing, nor does it isolate locking or RCU
> overhead. Allocation time includes the terminal failed request that detects
> exhaustion.
>
> Run the benchmark at several capacities to show how the approaches scale.
> Report allocation and free times separately because bitmap, IDA and Maple
> Tree removal have substantially different costs.
>
> On x86/kvm, the output example is:
>
> Start testing bitmap vs IDA vs Maple Tree region allocation
> memory: bitmap is exact; IDA and Maple Tree are lower bounds
> Type alloc (ns) free (ns) regions capacity memory (B)
> Bitmap 93457345 176151 60644 1000000 125000
> Maple 11758660 12870146 60644 1000000 1552656
> IDA 31066416 20870824 60644 1000000 134864
> Bitmap 919119 17679 6032 100000 12504
> Maple 1158193 1187140 6032 100000 154640
> IDA 2759670 2116004 6032 100000 14288
> Bitmap 17120 2043 613 10000 1256
> Maple 116350 117537 613 10000 15888
> IDA 243396 202654 613 10000 1872
> Bitmap 1220 262 55 1000 128
> Maple 12076 10106 55 1000 1552
> IDA 25730 20875 55 1000 144
> Bitmap 593 124 18 100 16
> Maple 3599 4782 18 100 528
> IDA 3266 1960 18 100 144
> Bitmap 414 129 10 10 8
> Maple 2143 1385 10 10 272
> IDA 892 648 10 10 16
> Region allocation benchmark complete
>
> Reported IDA and Maple Tree memory figures exclude slab overhead
> and transient allocations. The Maple Tree figure is additionally
> a lower-bound estimate that assumes fully occupied leaf nodes and
> excludes internal nodes.
>
> IDA has no region-allocation API, so each region is implemented as
> a sequence of single-ID allocations. The IDs remain contiguous
> because this benchmark fills an initially empty IDA monotonically.
>
> The benchmark is motivated by the discussion linked below about choosing
> the best data structure for the channel ID pool with the capacity of 2048
> IDs for the nova GPU driver.
>
> Specifically for 2048 IDs the result is:
>
> Bitmap 5112 615 121 2048 256
> Maple 78526 59592 121 2048 3344
> IDA 165274 117761 121 2048 848
>
> The benchmark accepts a list of up to 64 nonzero capacities to test.
> For example:
>
> insmod region_alloc_benchmark.ko capacities=1024,2048,4096,65536
>
> The list may contain duplicate capacities. Each occurrence generates a new
> region-size sequence, which is useful for collecting statistical
> characteristics of the benchmark results.
>
> Link: https://lore.kernel.org/all/20260710-chid-maple-v1-1-4ee869055268@nvidia.com/
> Signed-off-by: Yury Norov <ynorov@nvidia.com>
> ---
> v3:
> - allow capacities to be configured through the module parameter (Yury Norov)
> - document default and custom-capacity usage in Kconfig (Yury Norov)
> - store and generate only the region sizes required by each run (Yury Norov)
> - clarify the workload, timing semantics and memory estimates (Pedro Falcato, Onur Özkan)
> - report region count and capacity while retaining raw timings (Gary Guo)
> - add post-free integrity checks (Yury Norov)
> - add default capacities of 10 and 100 IDs (Matthew Wilcox)
> - bound generated region sizes appropriately for small capacities (Matthew Wilcox)
> - report successful completion and return -EAGAIN for repeat runs (Yury Norov)
> v2: https://lore.kernel.org/all/20260711063602.426311-1-ynorov@nvidia.com/
> v1: https://lore.kernel.org/all/20260711013910.349586-1-ynorov@nvidia.com/
>
> MAINTAINERS | 3 +
> lib/Kconfig.debug | 13 +++
> lib/Makefile | 1 +
> lib/region_alloc_benchmark.c | 217 +++++++++++++++++++++++++++++++++++
> 4 files changed, 234 insertions(+)
> create mode 100644 lib/region_alloc_benchmark.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7cc4bca5a2c5..9e487a94aba4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4615,6 +4615,7 @@ F: lib/bitmap.c
> F: lib/cpumask.c
> F: lib/find_bit.c
> F: lib/find_bit_benchmark.c
> +F: lib/region_alloc_benchmark.c
> F: lib/test_bitmap.c
> F: lib/tests/cpumask_kunit.c
> F: tools/include/linux/bitfield.h
> @@ -15581,6 +15582,7 @@ F: Documentation/core-api/maple_tree.rst
> F: include/linux/maple_tree.h
> F: include/trace/events/maple_tree.h
> F: lib/maple_tree.c
> +F: lib/region_alloc_benchmark.c
> F: lib/test_maple_tree.c
> F: rust/helpers/maple_tree.c
> F: rust/kernel/maple_tree.rs
> @@ -29323,6 +29325,7 @@ F: Documentation/core-api/xarray.rst
> F: include/linux/idr.h
> F: include/linux/xarray.h
> F: lib/idr.c
> +F: lib/region_alloc_benchmark.c
> F: lib/test_xarray.c
> F: lib/xarray.c
> F: tools/testing/radix-tree
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 1244dcac2294..0451dfca7098 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -2683,6 +2683,19 @@ config FIND_BIT_BENCHMARK
>
> If unsure, say N.
>
> +config REGION_ALLOC_BENCHMARK
> + tristate "Benchmark bitmap, IDA and Maple Tree region allocation"
> + help
> + This builds a microbenchmark comparing variable-sized region
> + allocation using bitmaps, IDA and Maple Tree. The benchmark
> + runs at initialization time.
> +
> + Usage:
> + insmod region_alloc_benchmark.ko
> + insmod region_alloc_benchmark.ko capacities=1024,2048,4096,65536
> +
> + If unsure, say N.
> +
> config FIND_BIT_BENCHMARK_RUST
> tristate "Test find_bit functions in Rust"
> depends on RUST
> diff --git a/lib/Makefile b/lib/Makefile
> index 7f75cc6edf94..adb18810e3f7 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -64,6 +64,7 @@ obj-y += hexdump.o
> obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o
> obj-y += kstrtox.o
> obj-$(CONFIG_FIND_BIT_BENCHMARK) += find_bit_benchmark.o
> +obj-$(CONFIG_REGION_ALLOC_BENCHMARK) += region_alloc_benchmark.o
> obj-$(CONFIG_FIND_BIT_BENCHMARK_RUST) += find_bit_benchmark_rust.o
> obj-$(CONFIG_TEST_BPF) += test_bpf.o
> test_dhry-objs := dhry_1.o dhry_2.o dhry_run.o
> diff --git a/lib/region_alloc_benchmark.c b/lib/region_alloc_benchmark.c
> new file mode 100644
> index 000000000000..e88b4cf55c62
> --- /dev/null
> +++ b/lib/region_alloc_benchmark.c
> @@ -0,0 +1,217 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Benchmark bitmap, IDA and Maple Tree allocation of variable-sized regions. */
> +
> +#include <linux/bitmap.h>
> +#include <linux/idr.h>
> +#include <linux/kernel.h>
> +#include <linux/maple_tree.h>
> +#include <linux/module.h>
> +#include <linux/printk.h>
> +#include <linux/random.h>
> +#include <linux/slab.h>
> +#include <linux/xarray.h>
> +
> +#define REGION_MAX_SIZE 32
> +
> +static unsigned long *bitmap __initdata;
> +/* One more request guarantees that even an all-ones trace reaches ENOSPC. */
> +static u8 *reg_sz __initdata;
> +static unsigned long *reg_idx __initdata;
> +static unsigned long capacities[64] = { 1000000, 100000, 10000, 1000, 100, 10 };
> +static unsigned int cap_cnt = 6;
> +
> +module_param_array(capacities, ulong, &cap_cnt, 0400);
> +MODULE_PARM_DESC(capacities, "Region capacities to benchmark");
> +
> +static unsigned long __init benchmark_bitmap(unsigned long cap)
> +{
> + unsigned long cnt, idx;
> + ktime_t alloc_time, free_time;
> + size_t sz;
> +
> + bitmap_zero(bitmap, cap);
> + alloc_time = ktime_get();
> + for (cnt = 0; cnt <= cap; cnt++) {
> + idx = bitmap_find_next_zero_area(bitmap, cap, 0, reg_sz[cnt], 0);
> + if (idx >= cap)
> + break;
> +
> + reg_idx[cnt] = idx;
> + bitmap_set(bitmap, idx, reg_sz[cnt]);
> + }
> + alloc_time = ktime_get() - alloc_time;
> +
> + idx = cnt;
> +
> + free_time = ktime_get();
> + while (idx--)
> + bitmap_clear(bitmap, reg_idx[idx], reg_sz[idx]);
> + free_time = ktime_get() - free_time;
> +
> + WARN_ON(!bitmap_empty(bitmap, cap));
> +
> + sz = BITS_TO_LONGS(cap) * sizeof(unsigned long);
> + pr_err("Bitmap %12llu %12llu %8lu %8lu %10zu\n",
> + alloc_time, free_time, cnt, cap, sz);
> +
> + return cnt;
> +}
> +
> +static size_t __init ida_size(unsigned long nr_ids)
> +{
> + unsigned long entries = DIV_ROUND_UP(nr_ids, IDA_BITMAP_BITS);
> + unsigned long bitmaps = nr_ids / IDA_BITMAP_BITS;
> + unsigned long nodes = 0;
> +
> + if (nr_ids % IDA_BITMAP_BITS > BITS_PER_XA_VALUE)
> + bitmaps++;
> +
> + while (entries > 1) {
> + entries = DIV_ROUND_UP(entries, XA_CHUNK_SIZE);
> + nodes += entries;
> + }
> +
> + return sizeof(struct ida) +
> + bitmaps * sizeof(struct ida_bitmap) +
> + nodes * sizeof(struct xa_node);
> +}
> +
> +static unsigned long __init benchmark_ida(unsigned long cap)
> +{
> + struct ida ida = IDA_INIT(ida);
> + unsigned long cnt, idx, off, nr_ids = 0;
> + ktime_t alloc_time, free_time;
> + int id = -ENOSPC;
> +
> + alloc_time = ktime_get();
> + for (cnt = 0; cnt <= cap; cnt++) {
> + for (off = 0; off < reg_sz[cnt]; off++) {
> + id = ida_alloc_max(&ida, cap - 1, GFP_KERNEL);
> + if (id < 0)
> + break;
> +
> + if (!off)
> + reg_idx[cnt] = id;
> + }
> + if (id < 0) {
> + while (off--)
> + ida_free(&ida, reg_idx[cnt] + off);
> + break;
> + }
> + WARN_ON(id != reg_idx[cnt] + reg_sz[cnt] - 1);
> + nr_ids += reg_sz[cnt];
> + }
> + alloc_time = ktime_get() - alloc_time;
> +
> + WARN_ON(id != -ENOSPC);
> +
> + idx = cnt;
> +
> + free_time = ktime_get();
> + while (idx--) {
> + for (off = 0; off < reg_sz[idx]; off++)
> + ida_free(&ida, reg_idx[idx] + off);
> + }
> + free_time = ktime_get() - free_time;
> +
> + WARN_ON(!ida_is_empty(&ida));
> +
> + pr_err("IDA %12llu %12llu %8lu %8lu %10zu\n",
> + alloc_time, free_time, cnt, cap, ida_size(nr_ids));
> +
> + ida_destroy(&ida);
> + return cnt;
> +}
> +
> +static unsigned long __init benchmark_maple_tree(unsigned long cap)
> +{
> + struct maple_tree mt = MTREE_INIT(mt, MT_FLAGS_ALLOC_RANGE);
> + unsigned long cnt, idx;
> + ktime_t alloc_time, free_time;
> + size_t sz;
> + int ret;
> +
> + alloc_time = ktime_get();
> + for (cnt = 0; cnt <= cap; cnt++) {
> + ret = mtree_alloc_range(&mt, &idx, xa_mk_value(cnt + 1),
> + reg_sz[cnt], 0, cap - 1, GFP_KERNEL);
> + if (ret)
> + break;
> +
> + reg_idx[cnt] = idx;
> + }
> + alloc_time = ktime_get() - alloc_time;
> +
> + WARN_ON(ret != -EBUSY);
> +
> + idx = cnt;
> +
> + free_time = ktime_get();
> + while (idx--)
> + mtree_erase(&mt, reg_idx[idx]);
> + free_time = ktime_get() - free_time;
> +
> + WARN_ON(!mtree_empty(&mt));
> +
> + /* Minimum storage assuming fully occupied allocation-range leaf nodes. */
> + sz = sizeof(mt) + DIV_ROUND_UP(cnt, MAPLE_ARANGE64_SLOTS) * sizeof(struct maple_node);
> + pr_err("Maple %12llu %12llu %8lu %8lu %10zu\n",
> + alloc_time, free_time, cnt, cap, sz);
> +
> + mtree_destroy(&mt);
> + return cnt;
> +}
> +
> +static int __init region_alloc_benchmark(void)
> +{
> + unsigned long bitmap_count, ida_count, maple_count;
> + unsigned long i, max_cap = 0;
> + int ret = -ENOMEM;
> +
> + for (i = 0; i < cap_cnt; i++) {
> + if (capacities[i] == 0) {
> + pr_err("capacity must be nonzero\n");
> + return -EINVAL;
> + }
> + max_cap = max(max_cap, capacities[i]);
> + }
> +
> + bitmap = kvmalloc_array(BITS_TO_LONGS(max_cap), sizeof(*bitmap), GFP_KERNEL);
> + reg_sz = kvmalloc_array(max_cap + 1, sizeof(*reg_sz), GFP_KERNEL);
> + reg_idx = kvmalloc_array(max_cap, sizeof(*reg_idx), GFP_KERNEL);
> + if (!bitmap || !reg_sz || !reg_idx)
> + goto out;
> +
> + pr_err("\nStart testing bitmap vs IDA vs Maple Tree region allocation\n");
> + pr_err("memory: bitmap is exact; IDA and Maple Tree are lower bounds\n");
> + pr_err("Type alloc (ns) free (ns) regions capacity memory (B)\n");
> +
> + for (i = 0; i < cap_cnt; i++) {
> + unsigned long idx, max_size;
> +
> + max_size = min(REGION_MAX_SIZE, capacities[i] / 10) ? : 1;
> + for (idx = 0; idx <= capacities[i]; idx++)
> + reg_sz[idx] = get_random_u32_below(max_size) + 1;
> +
> + bitmap_count = benchmark_bitmap(capacities[i]);
> + maple_count = benchmark_maple_tree(capacities[i]);
> + ida_count = benchmark_ida(capacities[i]);
> +
> + WARN_ON(bitmap_count != ida_count);
> + WARN_ON(bitmap_count != maple_count);
> + }
> +
> + /* Return an error so the benchmark can run repeatedly without rmmod. */
> + pr_info("Region allocation benchmark complete\n");
> + ret = -EAGAIN;
> +out:
> + kvfree(reg_idx);
> + kvfree(reg_sz);
> + kvfree(bitmap);
> + return ret;
> +}
> +module_init(region_alloc_benchmark);
> +
> +MODULE_AUTHOR("Yury Norov <ynorov@nvidia.com>");
> +MODULE_DESCRIPTION("Benchmark bitmap, IDA and Maple Tree region allocation");
> +MODULE_LICENSE("GPL");
> --
> 2.53.0
prev parent reply other threads:[~2026-07-22 15:04 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-17 5:32 [PATCH v3] lib: test bitmap vs IDA vs Maple Tree performance for region allocations Yury Norov
2026-07-22 15:04 ` Yury Norov [this message]
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=amDcBOmcxxz0bCOF@yury \
--to=ynorov@nvidia.com \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=airlied@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=aliceryhl@google.com \
--cc=andrewjballance@gmail.com \
--cc=apopple@nvidia.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=bqe@google.com \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=ecourtney@nvidia.com \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=jhubbard@nvidia.com \
--cc=liam@infradead.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux@rasmusvillemoes.dk \
--cc=lossin@kernel.org \
--cc=maple-tree@lists.infradead.org \
--cc=nova-gpu@lists.linux.dev \
--cc=ojeda@kernel.org \
--cc=pfalcato@suse.de \
--cc=rust-for-linux@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=ttabi@nvidia.com \
--cc=willy@infradead.org \
--cc=work@onurozkan.dev \
--cc=yury.norov@gmail.com \
--cc=zhiw@nvidia.com \
/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