Skip to content

Commit

Permalink
samples: lib: os: hash_map: add Hashmap sample code
Browse files Browse the repository at this point in the history
This sample shows how to use `sys_hashmap`.

Signed-off-by: Chris Friedt <cfriedt@meta.com>
  • Loading branch information
cfriedt authored and carlescufi committed Feb 22, 2023
1 parent e607684 commit 480b749
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 0 deletions.
10 changes: 10 additions & 0 deletions samples/basic/hash_map/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2022 Meta
#
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(hash_map)

target_sources(app PRIVATE src/main.c)
28 changes: 28 additions & 0 deletions samples/basic/hash_map/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright (c) 2022 Meta
#
# SPDX-License-Identifier: Apache-2.0

config TEST_LIB_HASH_MAP_MAX_ENTRIES
int "Maximum number of Hashmap entries"
default 40
help
When benchmarking the performance of the Hashmap, it helps to be able
to vary the number of entries to insert or remove from the hash table
in a convenient way. This option translates to MANY in the test sources.

CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES

Of course, using realloc(), we are limited by the amount of available
heap memory. For test scenarios using the Minimal C library, the heap
size is controlled via

CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE

For native_posix_64, the number of entries can be configured
independently of the arena size since the native libc is used.

config TEST_LIB_HASH_MAP_DURATION_S
int "Duration of test (in seconds)"
default 3

source "Kconfig.zephyr"
50 changes: 50 additions & 0 deletions samples/basic/hash_map/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
.. _system_hashmap:

System Hashmap
##############

Overview
********

This is a simple example that repeatedly

* inserts up to ``CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES``
* replaces up to the same number that were previously inserted
* removes all previously inserted keys

Building
********

This application can be built on native_posix as follows:

.. zephyr-app-commands::
:zephyr-app: samples/basic/hash_map
:host-os: unix
:board: native_posix
:goals: build
:compact:

To build for another board, change "native_posix" above to that board's name.

Additionally, it is possible to use one of the other Hashmap implementations by specifying

* ``CONFIG_SYS_HASH_MAP_CHOICE_SC=y`` (Separate Chaining)
* ``CONFIG_SYS_HASH_MAP_CHOICE_OA_LP=y`` (Open Addressing / Linear Probe)
* ``CONFIG_SYS_HASH_MAP_CHOICE_CXX=y`` (C Wrapper around the C++ ``std::unordered_map``)

To stress the Hashmap implementation, adjust ``CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES``.

Running
*******

Run ``build/zephyr/zephyr.exe``

Sample Output
*************

.. code-block:: console
System Hashmap sample
[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
[00:00:11.010,000] <inf> hashmap_sample: success
16 changes: 16 additions & 0 deletions samples/basic/hash_map/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (c) 2022 Meta
#
# SPDX-License-Identifier: Apache-2.0

CONFIG_BOOT_BANNER=n
CONFIG_LOG=y

CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=8192
CONFIG_NEWLIB_LIBC_MIN_REQUIRED_HEAP_SIZE=8192
CONFIG_PICOLIBC_HEAP_SIZE=8192

CONFIG_SYS_HASH_FUNC32=y
CONFIG_SYS_HASH_MAP=y

CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES=40
58 changes: 58 additions & 0 deletions samples/basic/hash_map/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright (c) 2022 Meta
#
# SPDX-License-Identifier: Apache-2.0

sample:
description: System Hashmap sample
name: System Hashmap sample

common:
min_ram: 24
integration_platforms:
- qemu_x86_64
- mps2_an385
harness: console
harness_config:
type: one_line
regex:
- .*success

tests:
# Minimal Libc
libraries.hash_map.minimal.separate_chaining.djb2:
extra_configs:
- CONFIG_MINIMAL_LIBC=y
- CONFIG_SYS_HASH_MAP_CHOICE_SC=y
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
libraries.hash_map.minimal.open_addressing.djb2:
extra_configs:
- CONFIG_MINIMAL_LIBC=y
- CONFIG_SYS_HASH_MAP_CHOICE_OA_LP=y
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
# Newlib
libraries.hash_map.newlib.separate_chaining.djb2:
extra_configs:
- CONFIG_NEWLIB_LIBC=y
- CONFIG_SYS_HASH_MAP_CHOICE_SC=y
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
libraries.hash_map.newlib.open_addressing.djb2:
extra_configs:
- CONFIG_NEWLIB_LIBC=y
- CONFIG_SYS_HASH_MAP_CHOICE_OA_LP=y
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
libraries.hash_map.newlib.cxx_unordered_map.djb2:
extra_configs:
- CONFIG_NEWLIB_LIBC=y
- CONFIG_SYS_HASH_MAP_CHOICE_CXX=y
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
# PicoLibc
libraries.hash_map.picolibc.separate_chaining.djb2:
extra_configs:
- CONFIG_PICOLIBC=y
- CONFIG_SYS_HASH_MAP_CHOICE_SC=y
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
libraries.hash_map.picolibc.open_addressing.djb2:
extra_configs:
- CONFIG_PICOLIBC=y
- CONFIG_SYS_HASH_MAP_CHOICE_OA_LP=y
- CONFIG_SYS_HASH_FUNC32_CHOICE_DJB2=y
103 changes: 103 additions & 0 deletions samples/basic/hash_map/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2022 Meta
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/hash_map.h>
#include <zephyr/random/rand32.h>

LOG_MODULE_REGISTER(hashmap_sample);

SYS_HASHMAP_DEFINE_STATIC(map);

void print_sys_memory_stats(void);

struct _stats {
uint64_t n_insert;
uint64_t n_remove;
uint64_t n_replace;
uint64_t n_error;
uint64_t n_miss;
size_t max_size;
};

static void print_stats(const struct _stats *stats);

void main(void)
{
size_t i;
int ires;
bool bres;
struct _stats stats = {0};

printk("CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES: %u\n", CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES);

do {
for (i = 0; i < CONFIG_TEST_LIB_HASH_MAP_MAX_ENTRIES; ++i) {

ires = sys_hashmap_insert(&map, i, i, NULL);
if (ires < 0) {
break;
}

__ASSERT(ires == 1, "Expected to insert %zu", i);
++stats.n_insert;
++stats.max_size;

LOG_DBG("Inserted %zu", i);

if (k_uptime_get() / MSEC_PER_SEC > CONFIG_TEST_LIB_HASH_MAP_DURATION_S) {
goto out;
}
}

for (i = 0; i < stats.max_size; ++i) {

ires = sys_hashmap_insert(&map, i, stats.max_size - i, NULL);
__ASSERT(ires == 0, "Failed to replace %zu", i);
++stats.n_replace;

LOG_DBG("Replaced %zu", i);

if (k_uptime_get() / MSEC_PER_SEC > CONFIG_TEST_LIB_HASH_MAP_DURATION_S) {
goto out;
}
}

for (i = stats.max_size; i > 0; --i) {
bres = sys_hashmap_remove(&map, i - 1, NULL);
__ASSERT(bres, "Failed to remove %zu", i - 1);
++stats.n_remove;

LOG_DBG("Removed %zu", i - 1);

if (k_uptime_get() / MSEC_PER_SEC > CONFIG_TEST_LIB_HASH_MAP_DURATION_S) {
goto out;
}
}
/* These architectures / boards seem to have trouble with basic timekeeping atm */
} while (!IS_ENABLED(CONFIG_ARCH_POSIX) && !IS_ENABLED(CONFIG_BOARD_QEMU_NIOS2));

out:

print_stats(&stats);

sys_hashmap_clear(&map, NULL, NULL);

LOG_INF("success");

if (IS_ENABLED(CONFIG_ARCH_POSIX)) {
exit(0);
}
}

static void print_stats(const struct _stats *stats)
{
LOG_INF("n_insert: %" PRIu64 " n_remove: %" PRIu64 " n_replace: %" PRIu64
" n_miss: %" PRIu64 " n_error: %" PRIu64 " max_size: %zu",
stats->n_insert, stats->n_remove, stats->n_replace, stats->n_miss, stats->n_error,
stats->max_size);
}

0 comments on commit 480b749

Please sign in to comment.