Skip to content

Commit 480b749

Browse files
Chris Friedtcarlescufi
authored andcommitted
samples: lib: os: hash_map: add Hashmap sample code
This sample shows how to use `sys_hashmap`. Signed-off-by: Chris Friedt <[email protected]>
1 parent e607684 commit 480b749

File tree

6 files changed

+265
-0
lines changed

6 files changed

+265
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2022 Meta
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
cmake_minimum_required(VERSION 3.20.0)
6+
7+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
8+
project(hash_map)
9+
10+
target_sources(app PRIVATE src/main.c)

samples/basic/hash_map/Kconfig

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright (c) 2022 Meta
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
config TEST_LIB_HASH_MAP_MAX_ENTRIES
6+
int "Maximum number of Hashmap entries"
7+
default 40
8+
help
9+
When benchmarking the performance of the Hashmap, it helps to be able
10+
to vary the number of entries to insert or remove from the hash table
11+
in a convenient way. This option translates to MANY in the test sources.
12+
13+
CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES
14+
15+
Of course, using realloc(), we are limited by the amount of available
16+
heap memory. For test scenarios using the Minimal C library, the heap
17+
size is controlled via
18+
19+
CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE
20+
21+
For native_posix_64, the number of entries can be configured
22+
independently of the arena size since the native libc is used.
23+
24+
config TEST_LIB_HASH_MAP_DURATION_S
25+
int "Duration of test (in seconds)"
26+
default 3
27+
28+
source "Kconfig.zephyr"

samples/basic/hash_map/README.rst

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
.. _system_hashmap:
2+
3+
System Hashmap
4+
##############
5+
6+
Overview
7+
********
8+
9+
This is a simple example that repeatedly
10+
11+
* inserts up to ``CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES``
12+
* replaces up to the same number that were previously inserted
13+
* removes all previously inserted keys
14+
15+
Building
16+
********
17+
18+
This application can be built on native_posix as follows:
19+
20+
.. zephyr-app-commands::
21+
:zephyr-app: samples/basic/hash_map
22+
:host-os: unix
23+
:board: native_posix
24+
:goals: build
25+
:compact:
26+
27+
To build for another board, change "native_posix" above to that board's name.
28+
29+
Additionally, it is possible to use one of the other Hashmap implementations by specifying
30+
31+
* ``CONFIG_SYS_HASH_MAP_CHOICE_SC=y`` (Separate Chaining)
32+
* ``CONFIG_SYS_HASH_MAP_CHOICE_OA_LP=y`` (Open Addressing / Linear Probe)
33+
* ``CONFIG_SYS_HASH_MAP_CHOICE_CXX=y`` (C Wrapper around the C++ ``std::unordered_map``)
34+
35+
To stress the Hashmap implementation, adjust ``CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES``.
36+
37+
Running
38+
*******
39+
40+
Run ``build/zephyr/zephyr.exe``
41+
42+
Sample Output
43+
*************
44+
45+
.. code-block:: console
46+
47+
System Hashmap sample
48+
49+
[00:00:11.000,000] <inf> hashmap_sample: n_insert: 118200 n_remove: 295500 n_replace: 329061 n_miss: 0 n_error: 0 max_size: 118200
50+
[00:00:11.010,000] <inf> hashmap_sample: success

samples/basic/hash_map/prj.conf

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright (c) 2022 Meta
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
CONFIG_BOOT_BANNER=n
6+
CONFIG_LOG=y
7+
8+
CONFIG_TEST_RANDOM_GENERATOR=y
9+
CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=8192
10+
CONFIG_NEWLIB_LIBC_MIN_REQUIRED_HEAP_SIZE=8192
11+
CONFIG_PICOLIBC_HEAP_SIZE=8192
12+
13+
CONFIG_SYS_HASH_FUNC32=y
14+
CONFIG_SYS_HASH_MAP=y
15+
16+
CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES=40

samples/basic/hash_map/sample.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright (c) 2022 Meta
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
sample:
6+
description: System Hashmap sample
7+
name: System Hashmap sample
8+
9+
common:
10+
min_ram: 24
11+
integration_platforms:
12+
- qemu_x86_64
13+
- mps2_an385
14+
harness: console
15+
harness_config:
16+
type: one_line
17+
regex:
18+
- .*success
19+
20+
tests:
21+
# Minimal Libc
22+
libraries.hash_map.minimal.separate_chaining.djb2:
23+
extra_configs:
24+
- CONFIG_MINIMAL_LIBC=y
25+
- CONFIG_SYS_HASH_MAP_CHOICE_SC=y
26+
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
27+
libraries.hash_map.minimal.open_addressing.djb2:
28+
extra_configs:
29+
- CONFIG_MINIMAL_LIBC=y
30+
- CONFIG_SYS_HASH_MAP_CHOICE_OA_LP=y
31+
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
32+
# Newlib
33+
libraries.hash_map.newlib.separate_chaining.djb2:
34+
extra_configs:
35+
- CONFIG_NEWLIB_LIBC=y
36+
- CONFIG_SYS_HASH_MAP_CHOICE_SC=y
37+
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
38+
libraries.hash_map.newlib.open_addressing.djb2:
39+
extra_configs:
40+
- CONFIG_NEWLIB_LIBC=y
41+
- CONFIG_SYS_HASH_MAP_CHOICE_OA_LP=y
42+
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
43+
libraries.hash_map.newlib.cxx_unordered_map.djb2:
44+
extra_configs:
45+
- CONFIG_NEWLIB_LIBC=y
46+
- CONFIG_SYS_HASH_MAP_CHOICE_CXX=y
47+
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
48+
# PicoLibc
49+
libraries.hash_map.picolibc.separate_chaining.djb2:
50+
extra_configs:
51+
- CONFIG_PICOLIBC=y
52+
- CONFIG_SYS_HASH_MAP_CHOICE_SC=y
53+
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
54+
libraries.hash_map.picolibc.open_addressing.djb2:
55+
extra_configs:
56+
- CONFIG_PICOLIBC=y
57+
- CONFIG_SYS_HASH_MAP_CHOICE_OA_LP=y
58+
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y

samples/basic/hash_map/src/main.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright (c) 2022 Meta
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/logging/log.h>
9+
#include <zephyr/sys/hash_map.h>
10+
#include <zephyr/random/rand32.h>
11+
12+
LOG_MODULE_REGISTER(hashmap_sample);
13+
14+
SYS_HASHMAP_DEFINE_STATIC(map);
15+
16+
void print_sys_memory_stats(void);
17+
18+
struct _stats {
19+
uint64_t n_insert;
20+
uint64_t n_remove;
21+
uint64_t n_replace;
22+
uint64_t n_error;
23+
uint64_t n_miss;
24+
size_t max_size;
25+
};
26+
27+
static void print_stats(const struct _stats *stats);
28+
29+
void main(void)
30+
{
31+
size_t i;
32+
int ires;
33+
bool bres;
34+
struct _stats stats = {0};
35+
36+
printk("CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES: %u\n", CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES);
37+
38+
do {
39+
for (i = 0; i < CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES; ++i) {
40+
41+
ires = sys_hashmap_insert(&map, i, i, NULL);
42+
if (ires < 0) {
43+
break;
44+
}
45+
46+
__ASSERT(ires == 1, "Expected to insert %zu", i);
47+
++stats.n_insert;
48+
++stats.max_size;
49+
50+
LOG_DBG("Inserted %zu", i);
51+
52+
if (k_uptime_get() / MSEC_PER_SEC > CONFIG_TEST_LIB_HASH_MAP_DURATION_S) {
53+
goto out;
54+
}
55+
}
56+
57+
for (i = 0; i < stats.max_size; ++i) {
58+
59+
ires = sys_hashmap_insert(&map, i, stats.max_size - i, NULL);
60+
__ASSERT(ires == 0, "Failed to replace %zu", i);
61+
++stats.n_replace;
62+
63+
LOG_DBG("Replaced %zu", i);
64+
65+
if (k_uptime_get() / MSEC_PER_SEC > CONFIG_TEST_LIB_HASH_MAP_DURATION_S) {
66+
goto out;
67+
}
68+
}
69+
70+
for (i = stats.max_size; i > 0; --i) {
71+
bres = sys_hashmap_remove(&map, i - 1, NULL);
72+
__ASSERT(bres, "Failed to remove %zu", i - 1);
73+
++stats.n_remove;
74+
75+
LOG_DBG("Removed %zu", i - 1);
76+
77+
if (k_uptime_get() / MSEC_PER_SEC > CONFIG_TEST_LIB_HASH_MAP_DURATION_S) {
78+
goto out;
79+
}
80+
}
81+
/* These architectures / boards seem to have trouble with basic timekeeping atm */
82+
} while (!IS_ENABLED(CONFIG_ARCH_POSIX) && !IS_ENABLED(CONFIG_BOARD_QEMU_NIOS2));
83+
84+
out:
85+
86+
print_stats(&stats);
87+
88+
sys_hashmap_clear(&map, NULL, NULL);
89+
90+
LOG_INF("success");
91+
92+
if (IS_ENABLED(CONFIG_ARCH_POSIX)) {
93+
exit(0);
94+
}
95+
}
96+
97+
static void print_stats(const struct _stats *stats)
98+
{
99+
LOG_INF("n_insert: %" PRIu64 " n_remove: %" PRIu64 " n_replace: %" PRIu64
100+
" n_miss: %" PRIu64 " n_error: %" PRIu64 " max_size: %zu",
101+
stats->n_insert, stats->n_remove, stats->n_replace, stats->n_miss, stats->n_error,
102+
stats->max_size);
103+
}

0 commit comments

Comments
 (0)