things
This commit is contained in:
parent
4aefe8b42d
commit
94837ed410
14 changed files with 1196 additions and 0 deletions
219
cache.c
Normal file
219
cache.c
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
// LRU cache:
|
||||
/*
|
||||
* The cache uses a pool (array) containing all the elements and a hash table
|
||||
* associating the position in the pool with the element id
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ugui.h"
|
||||
|
||||
// To have less collisions TABLE_SIZE has to be larger than the cache size
|
||||
#define CACHE_SIZE 265
|
||||
#define TABLE_SIZE (CACHE_SIZE * 1.5f)
|
||||
#define CACHE_NCYCLES (CACHE_SIZE * 2 / 3)
|
||||
#define CACHE_BSIZE (((CACHE_SIZE + 0x3f) & (~0x3f)) >> 6)
|
||||
#define CACHE_BRESET(b) \
|
||||
for (int i = 0; i < CACHE_BSIZE; b[i++] = 0) \
|
||||
;
|
||||
#define CACHE_BSET(b, x) b[(x) >> 6] |= (uint64_t)1 << ((x)&63)
|
||||
#define CACHE_BTEST(b, x) (b[(x) >> 6] & ((uint64_t)1 << ((x)&63)))
|
||||
|
||||
/* Hash Table Implementation ----------------------------------------------------- */
|
||||
|
||||
#define HASH_MAXSIZE 4096
|
||||
|
||||
// hash table (id -> index)
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint32_t index;
|
||||
} IdElem;
|
||||
|
||||
typedef struct {
|
||||
uint32_t items, size, exp;
|
||||
IdElem bucket[];
|
||||
} IdTable;
|
||||
|
||||
IdTable *table_create(uint32_t size)
|
||||
{
|
||||
if (!size || size > HASH_MAXSIZE) {
|
||||
return NULL;
|
||||
}
|
||||
/* round to the greater power of two */
|
||||
/* FIXME: check for intger overflow here */
|
||||
uint32_t exp = 32 - __builtin_clz(size - 1);
|
||||
size = 1 << (exp);
|
||||
/* FIXME: check for intger overflow here */
|
||||
IdTable *ht = malloc(sizeof(IdTable) + sizeof(IdElem) * size);
|
||||
if (ht) {
|
||||
ht->items = 0;
|
||||
ht->size = size;
|
||||
memset(ht->bucket, 0, sizeof(IdTable) * size);
|
||||
}
|
||||
return ht;
|
||||
}
|
||||
|
||||
void table_destroy(IdTable *ht)
|
||||
{
|
||||
if (ht) {
|
||||
free(ht);
|
||||
}
|
||||
}
|
||||
|
||||
// Find and return the element by pointer
|
||||
IdElem *table_search(IdTable *ht, uint32_t id)
|
||||
{
|
||||
if (!ht) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// In this case id is the hash
|
||||
uint32_t idx = id % ht->size;
|
||||
|
||||
for (uint32_t x = 0, i; x < ht->size; x++) {
|
||||
i = (idx + x) % ht->size;
|
||||
if (ht->bucket[i].id == 0 || ht->bucket[i].id == id) {
|
||||
return &(ht->bucket[i]);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// FIXME: this simply overrides the found item
|
||||
IdElem *table_insert(IdTable *ht, IdElem entry)
|
||||
{
|
||||
IdElem *r = table_search(ht, entry.id);
|
||||
if (r != NULL) {
|
||||
if (r->id != 0) {
|
||||
ht->items++;
|
||||
}
|
||||
*r = entry;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
IdElem *table_remove(IdTable *ht, uint32_t id)
|
||||
{
|
||||
if (!ht) {
|
||||
return NULL;
|
||||
}
|
||||
IdElem *r = table_search(ht, id);
|
||||
if (r) {
|
||||
r->id = 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Cache Implementation ---------------------------------------------------------- */
|
||||
|
||||
// Every CACHE_CYCLES operations mark not-present the unused elements
|
||||
#define CACHE_CYCLE(c) \
|
||||
do { \
|
||||
if (++(c->cycles) > CACHE_NCYCLES) { \
|
||||
for (int i = 0; i < CACHE_BSIZE; i++) { \
|
||||
c->present[i] &= c->used[i]; \
|
||||
c->used[i] = 0; \
|
||||
} \
|
||||
c->cycles = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
typedef struct {
|
||||
IdTable *table;
|
||||
UgElem *array;
|
||||
uint64_t *present, *used;
|
||||
int cycles;
|
||||
} UgElemCache;
|
||||
|
||||
/* FIXME: check for allocation errors */
|
||||
UgElemCache ug_cache_init(void)
|
||||
{
|
||||
IdTable *t = table_create(TABLE_SIZE);
|
||||
UgElem *a = malloc(sizeof(UgElem) * CACHE_SIZE);
|
||||
uint64_t *p = malloc(sizeof(uint64_t) * CACHE_BSIZE);
|
||||
uint64_t *u = malloc(sizeof(uint64_t) * CACHE_BSIZE);
|
||||
CACHE_BRESET(p);
|
||||
CACHE_BRESET(u);
|
||||
return (UgElemCache) {.table = t, .array = a, .present = p, .used = u, 0};
|
||||
}
|
||||
|
||||
void ug_cache_free(UgElemCache *cache)
|
||||
{
|
||||
if (cache) {
|
||||
table_destroy(cache->table);
|
||||
free(cache->array);
|
||||
free(cache->present);
|
||||
free(cache->used);
|
||||
}
|
||||
}
|
||||
|
||||
const UgElem *ug_cache_search(UgElemCache *cache, uint32_t id)
|
||||
{
|
||||
if (!cache) {
|
||||
return NULL;
|
||||
}
|
||||
IdElem *r;
|
||||
r = table_search(cache->table, id);
|
||||
/* MISS */
|
||||
if (!r || id != r->id) {
|
||||
return NULL;
|
||||
}
|
||||
/* MISS, the data is not valid (not present) */
|
||||
if (!CACHE_BTEST(cache->present, r->index)) {
|
||||
return NULL;
|
||||
}
|
||||
/* HIT, set as recently used */
|
||||
CACHE_BSET(cache->used, r->index);
|
||||
return (&cache->array[r->index]);
|
||||
}
|
||||
|
||||
/* Look for a free spot in the present bitmap and return its index */
|
||||
/* If there is no free space left then just return the first position */
|
||||
int ug_cache_get_free_spot(UgElemCache *cache)
|
||||
{
|
||||
if (!cache) {
|
||||
return -1;
|
||||
}
|
||||
int x = 0;
|
||||
for (int b = 0; b < CACHE_BSIZE; b++) {
|
||||
if (cache->present[b] == 0) {
|
||||
x = 64;
|
||||
} else {
|
||||
x = __builtin_clzll(cache->present[b]);
|
||||
}
|
||||
x = 64 - x;
|
||||
if (!CACHE_BTEST(cache->present, x + 64 * b)) {
|
||||
return x + 64 * b;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const UgElem *ug_cache_insert_at(UgElemCache *cache, const UgElem *g, uint32_t index)
|
||||
{
|
||||
if (!cache) {
|
||||
return NULL;
|
||||
}
|
||||
UgElem *spot = NULL;
|
||||
|
||||
/* Set used and present */
|
||||
CACHE_BSET(cache->present, index);
|
||||
CACHE_BSET(cache->used, index);
|
||||
CACHE_CYCLE(cache);
|
||||
spot = &(cache->array[index]);
|
||||
*spot = *g;
|
||||
IdElem e = {.id = g->id, .index = index};
|
||||
if (!table_insert(cache->table, e)) {
|
||||
return NULL;
|
||||
}
|
||||
return spot;
|
||||
}
|
||||
|
||||
// Insert an element in the cache
|
||||
const UgElem *ug_cache_insert(UgElemCache *cache, const UgElem *g, int32_t *index)
|
||||
{
|
||||
*index = ug_cache_get_free_spot(cache);
|
||||
return ug_cache_insert_at(cache, g, *index);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue