Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sys_heap: a new/simpler/faster memory allocator #17628

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions include/sys/sys_heap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_SYS_SYS_HEAP_H_
#define ZEPHYR_INCLUDE_SYS_SYS_HEAP_H_

#include <kernel.h>

/* Simple, fast heap implementation.
*
* A more or less conventional segregated fit allocator with
* power-of-two buckets.
*
* Excellent space efficiency. Chunks can be split arbitrarily in 8
* byte units. Overhead is only four bytes per allocated chunk (eight
* bytes for heaps >256kb or on 64 bit systems), plus a log2-sized
* array of 2-word bucket headers. No coarse alignment restrictions
* on blocks, they can be split and merged (in units of 8 bytes)
* arbitrarily.
*
* Simple API. Initialize at runtime with any blob of memory and not
* a macro-generated, carefully aligned static array. Allocate and
* free by user pointer and not an opaque block handle.
*
* Good fragmentation resistance. Freed blocks are always immediately
* merged with adjacent free blocks. Allocations are attempted from a
* sample of the smallest bucket that might fit, falling back rapidly
* to the smallest block guaranteed to fit. Split memory remaining in
* the chunk is always returned immediately to the heap for other
* allocation.
*
* Excellent performance with firmly bounded runtime. All operations
* are constant time (though there is a search of the smallest bucket
* that has a compile-time-configurable upper bound, setting this to
* extreme values results in an effectively linear search of the
* list), objectively fast (~hundred instructions) and and amenable to
* locked operation.
*/

/* Note: the init_mem/bytes fields are for the static initializer to
* have somewhere to put the arguments. The actual heap metadata at
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... forgot to remove this bit. Ignore this comment and those fields. I yanked the static initializer from this version entirely because the semantics of SYS_MEM_POOL_DEFINE is a little too weird to wrap anyway.

* runtime lives in the heap memory itself and this struct simply
* functions as an opaque pointer. Would be good to clean this up and
* put the two values somewhere else, though it would make
* SYS_HEAP_DEFINE a little hairy to write.
*/
struct sys_heap {
struct z_heap *heap;
void *init_mem;
size_t init_bytes;
};

struct z_heap_stress_result {
u32_t total_allocs;
u32_t successful_allocs;
u32_t total_frees;
u64_t accumulated_in_use_bytes;
};

/** @brief Initialize sys_heap
*
* Initializes a sys_heap struct to manage the specified memory.
*
* @param h Heap to initialize
* @param mem Untyped pointer to unused memory
* @param bytes Size of region pointed to by @a mem
*/
void sys_heap_init(struct sys_heap *h, void *mem, size_t bytes);

/** @brief Allocate memory from a sys_heap
*
* Returns a pointer to a block of unused memory in the heap. This
* memory will not otherwise be used until it is freed with
* sys_heap_free(). If no memory can be allocated, NULL will be
* returned.
*
* @note The sys_heap implementation is not internally synchronized.
* No two sys_heap functions should operate on the same heap at the
* same time. All locking must be provided by the user.
*
* @param h Heap from which to allocate
* @param bytes Number of bytes requested
* @return Pointer to memory the caller can now use
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO it would be good to add information about alignment of allocated memory.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normal malloc returns a pointer that is "suitably aligned for any kind of variable". I think we should to, otherwise there will be a problems

*/
void *sys_heap_alloc(struct sys_heap *h, size_t bytes);

/** @brief Free memory into a sys_heap
*
* De-allocates a pointer to memory previously returned from
* sys_heap_alloc such that it can be used for other purposes. The
* caller must not use the memory region after entry to this function.
*
* @note The sys_heap implementation is not internally synchronized.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that would mean lock should be taken by k_malloc

* No two sys_heap functions should operate on the same heap at the
* same time. All locking must be provided by the user.
*
* @param h Heap to which to return the memory
* @param mem A pointer previously returned from sys_heap_alloc()
*/
void sys_heap_free(struct sys_heap *h, void *mem);

/** @brief Validate heap integrity
*
* Validates the internal integrity of a sys_heap. Intended for unit
* test and validation code, though potentially useful as a user API
* for applications with complicated runtime reliability requirements.
* Note: this cannot catch every possible error, but if it returns
* true then the heap is in a consistent state and can correctly
* handle any sys_heap_alloc() request and free any live pointer
* returned from a previou allocation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previou

*
* @param h Heap to validate
* @return true, if the heap is valid, otherwise false
*/
bool sys_heap_validate(struct sys_heap *h);

/** @brief sys_heap stress test rig
*
* Test rig for heap allocation validation. This will loop for @a
* op_count cycles, in each iteration making a random choice to
* allocate or free a pointer of randomized (power law) size based on
* heuristics designed to keep the heap in a state where it is near @a
* target_percent full. Allocation and free operations are provided
* by the caller as callbacks (i.e. this can in theory test any heap).
* Results, including counts of frees and successfull/unsuccessful
* allocations, are returnewd via the @result struct.
*
* @param alloc Callback to perform an allocation. Passes back the @a
* arg parameter as a context handle.
* @param free Callback to perform a free of a pointer returned from
* @a alloc. Passes back the @a arg parameter as a
* context handle.
* @param arg Context handle to pass back to the callbacks
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change arg to context.

* @param total_bytes Size of the byte array the heap was initialized in
* @param op_count How many iterations to test
* @param scratch_mem A pointer to scratch memory to be used by the
* test. Should be about 1/2 the size of the heap
* for tests that need to stress fragmentation.
* @param scratch_bytes Size of the memory pointed to by @a scratch_mem
* @param target_percent Percentage fill value (1-100) to which the
* random allocation choices will seek. High
* values will result in significant allocation
* failures and a very fragmented heap.
* @param result Struct into which to store test results.
*/
void sys_heap_stress(void *(*alloc)(void *arg, size_t bytes),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wonder if this is a good place to have that api, after all it is API for testing (same with sys_heap_validate). What about having sys_heap_validate.h or something similar and not spoil this header.

void (*free)(void *arg, void *p),
void *arg, size_t total_bytes,
u32_t op_count,
void *scratch_mem, size_t scratch_bytes,
int target_percent,
struct z_heap_stress_result *result);

#endif /* ZEPHYR_INCLUDE_SYS_SYS_HEAP_H_ */
2 changes: 2 additions & 0 deletions lib/os/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ zephyr_sources(
thread_entry.c
timeutil.c
work_q.c
heap.c
heap-validate.c
)

zephyr_sources_ifdef(CONFIG_JSON_LIBRARY json.c)
Expand Down
25 changes: 25 additions & 0 deletions lib/os/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,29 @@ config BASE64
help
Enable base64 encoding and decoding functionality

config SYS_HEAP_VALIDATE
bool "Enable internal heap validity checking"
help
The sys_heap implementation is instrumented for extensive
internal validation. Leave this off by default, unless
modifying the heap code or (maybe) when running in
environments that require sensitive detection of memory
corruption.

config SYS_HEAP_ALLOC_LOOPS
int "Number of tries in the inner heap allocation loop"
default 3
help
The sys_heap allocator bounds the number of tries from the
smallest chunk level (the one that might not fit the
requested allocation) to maintain constant time performance.
Setting this to a high level will cause the heap to return
more successful allocations in situations of high
fragmentation, at the cost of potentially significant
(linear time) searching of the free list. The default is
three, which results in an allocator with good statistical
properties ("most" allocations that fit will succeed) but
keeps the maximum runtime at a tight bound so that the heap
is useful in locked or ISR contexts.

endmenu
Loading