mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 14:20:59 +00:00
0a847f92f1
This reduces the overhead needed to manage the memory blocks as the blocks are guaranteed to be page-aligned. Also, the superblock is now alllocated from within one of the memory blocks it manages. While this does slightly reduce the available cachelines within the first block (by one or two depending on 32 vs 64 bit pointers), it removes the need for an extra memory allocation (probably via malloc) for the superblock.
153 lines
4.6 KiB
C
153 lines
4.6 KiB
C
/*
|
|
cmem.h
|
|
|
|
Cache-line aligned memory allocator
|
|
|
|
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifndef __QF_cmem_h
|
|
#define __QF_cmem_h
|
|
|
|
#include "QF/qtypes.h"
|
|
|
|
#define MEM_LINE_SIZE 64
|
|
#define MAX_CACHE_LINES 9
|
|
|
|
typedef struct memline_s {
|
|
/* chain of free line blocks for fast allocation
|
|
* chain begins in memsuper_t
|
|
*/
|
|
struct memline_s *free_next;
|
|
struct memline_s **free_prev;
|
|
/* chain of free line blocks within a membock for merging
|
|
* chain begins in memblock_t
|
|
*/
|
|
struct memline_s *block_next;
|
|
struct memline_s **block_prev;
|
|
size_t size;
|
|
/* owning block
|
|
*/
|
|
struct memblock_s *block;
|
|
size_t pad[2];
|
|
} __attribute__((aligned (64))) memline_t;
|
|
|
|
typedef struct memsline_s {
|
|
struct memsline_s *next;
|
|
size_t size:2;
|
|
size_t list:4;
|
|
size_t prev:8 * sizeof (void *) - 6; // memsline_t **
|
|
} memsline_t;
|
|
|
|
typedef struct memblock_s {
|
|
struct memblock_s *next;
|
|
struct memblock_s **prev;
|
|
memline_t *free_lines;
|
|
/* Size of memory available within the page-sized block.
|
|
*/
|
|
size_t size;
|
|
/* Size of memory region after the page-sized block.
|
|
*
|
|
* Will be 0 for blocks that were allocated exclusively for small
|
|
* allocations, otherwise indicates the size of the allocated block.
|
|
*/
|
|
size_t post_size;
|
|
/* True if the post-header block is free to be reused.
|
|
*/
|
|
int post_free;
|
|
#if __WORDSIZE == 64
|
|
int pad;
|
|
#endif
|
|
size_t allocated;
|
|
} __attribute__((aligned (64))) memblock_t;
|
|
|
|
typedef struct memsuper_s {
|
|
size_t page_size;
|
|
size_t page_mask;
|
|
memblock_t *memblocks;
|
|
/* Allocated cache lines from which smaller blocks can be allocated.
|
|
*
|
|
* The index is the base-2 log minus 2 of the size of the elements in the
|
|
* cache line from which an element was last freed. Only 4-32 bytes are of
|
|
* interest because nothing smaller than 4 bytes (int/float) will be
|
|
* allocated, and 64 bytes and up consume entire cache lines.
|
|
*/
|
|
memsline_t *last_freed[4];
|
|
/* Free chache lines grouped by size.
|
|
*
|
|
* The index is the base-2 log of the MINIMUM number of cache lines
|
|
* available in each block. ie, blocks with 4, 5, 6 and 7 lines will all
|
|
* be in the third list (index 2). For 4k page sizes, only 6 lists are
|
|
* needed (32-63 lines) because a page can hold only 62 lines (1 for the
|
|
* control block and one to avoid a cache-line being on a page boundary).
|
|
* Having 9 (MAX_CACHE_LINES) lists allows page sizes up to 16kB.
|
|
*/
|
|
memline_t *free_lines[MAX_CACHE_LINES];
|
|
} memsuper_t;
|
|
|
|
memsuper_t *new_memsuper (void);
|
|
void delete_memsuper (memsuper_t *super);
|
|
void *cmemalloc (memsuper_t *super, size_t size);
|
|
void cmemfree (memsuper_t *super, void *mem);
|
|
|
|
/** High-tide structure allocator for use in linked lists.
|
|
|
|
Using a free-list with the name of \c NAME_freelist, return a single
|
|
element.
|
|
The type of the element must be a structure with a field named \c next.
|
|
When the free-list is empty, memory is claimed from the system in blocks.
|
|
Elements may be returned to the pool by linking them into the free-list.
|
|
|
|
\param s The number of structures in the block.
|
|
\param t The structure type.
|
|
\param n The \c NAME portion of the \c NAME_freelist free-list.
|
|
\param super The memsuper_t super block from which to allocate memory.
|
|
|
|
\hideinitializer
|
|
*/
|
|
#define CMEMALLOC(s, t, n, super) \
|
|
({ \
|
|
if (!n##_freelist) { \
|
|
int i; \
|
|
n##_freelist = cmemalloc ((super), (s) * sizeof (t)); \
|
|
for (i = 0; i < (s) - 1; i++) \
|
|
n##_freelist[i].next = &n##_freelist[i + 1]; \
|
|
n##_freelist[i].next = 0; \
|
|
} \
|
|
t *v = n##_freelist; \
|
|
n##_freelist = n##_freelist->next; \
|
|
v; \
|
|
})
|
|
|
|
/** Free a block allocated by #ALLOC
|
|
|
|
\param n The \c NAME portion of the \c NAME_freelist free-list.
|
|
\param p The pointer to the block to be freed.
|
|
|
|
\hideinitializer
|
|
*/
|
|
#define CMEMFREE(n, p) \
|
|
do { \
|
|
p->next = n##_freelist; \
|
|
n##_freelist = p; \
|
|
} while (0)
|
|
|
|
#endif//__QF_cmem_h
|