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

Map improve remove speed for large map and high percentage of element removed #608

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
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
113 changes: 1 addition & 112 deletions src/std/maps.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,118 +24,7 @@
# pragma warning(disable:4034) // sizeof(void) == 0
#endif

#define H_SIZE_INIT 3

// successive primes that double every time
static int H_PRIMES[] = {
7,17,37,79,163,331,673,1361,2729,5471,10949,21911,43853,87613,175229,350459,700919,1401857,2803727,5607457,11214943,22429903,44859823,89719661,179424673,373587883,776531401,1611623773
};

// ----- FREE LIST ---------------------------------

typedef struct {
int pos;
int count;
} hl_free_bucket;

typedef struct {
hl_free_bucket *buckets;
int head;
int nbuckets;
} hl_free_list;

static void hl_freelist_resize( hl_free_list *f, int newsize ) {
hl_free_bucket *buckets = (hl_free_bucket*)hl_gc_alloc_noptr(sizeof(hl_free_bucket)*newsize);
memcpy(buckets,f->buckets,f->head * sizeof(hl_free_bucket));
f->buckets = buckets;
f->nbuckets = newsize;
}

static void hl_freelist_init( hl_free_list *f ) {
memset(f,0,sizeof(hl_free_list));
}

static void hl_freelist_add_range( hl_free_list *f, int pos, int count ) {
hl_free_bucket *b = f->buckets;
hl_free_bucket *prev = NULL;
if( !b ) {
// special handling for countinuous space
if( f->nbuckets == 0 ) {
f->head = pos;
f->nbuckets = count;
return;
} else if( f->head + f->nbuckets == pos ) {
f->nbuckets += count;
return;
} else if( pos + count == f->head ) {
f->head -= count;
f->nbuckets += count;
return;
} else {
int cur_pos = f->head, cur_count = f->nbuckets;
f->head = 0;
f->nbuckets = 0;
hl_freelist_resize(f,2);
if( cur_count ) hl_freelist_add_range(f,cur_pos,cur_count);
b = f->buckets;
}
}
while( b < f->buckets + f->head ) {
if( b->pos > pos ) break;
prev = b;
b++;
}
if( b < f->buckets + f->head && b->pos == pos + count ) {
b->pos -= count;
b->count += count;
// merge
if( prev && prev->pos + prev->count == b->pos ) {
prev->count += b->count;
memmove(b,b+1,((f->buckets + f->head) - (b+1)) * sizeof(hl_free_bucket));
f->head--;
}
return;
}
if( prev && prev->pos + prev->count == pos ) {
prev->count += count;
return;
}
// insert
if( f->head == f->nbuckets ) {
int pos = (int)(b - f->buckets);
hl_freelist_resize(f,((f->nbuckets * 3) + 1) >> 1);
b = f->buckets + pos;
}
memmove(b+1,b,((f->buckets + f->head) - b) * sizeof(hl_free_bucket));
b->pos = pos;
b->count = count;
f->head++;
}

static void hl_freelist_add( hl_free_list *f, int pos ) {
hl_freelist_add_range(f,pos,1);
}

static int hl_freelist_get( hl_free_list *f ) {
hl_free_bucket *b;
int p;
if( !f->buckets ) {
if( f->nbuckets == 0 ) return -1;
f->nbuckets--;
return f->head++;
}
if( f->head == 0 )
return -1;
b = f->buckets + f->head - 1;
b->count--;
p = b->pos + b->count;
if( b->count == 0 ) {
f->head--;
if( f->head < (f->nbuckets>>1) )
hl_freelist_resize(f,f->nbuckets>>1);
}
return p;
}
#define H_SIZE_INIT 8

#define _MVAL_TYPE vdynamic*

Expand Down
187 changes: 85 additions & 102 deletions src/std/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,17 @@
#define t_map _MNAME(_map)
#define t_entry _MNAME(_entry)
#define t_value _MNAME(_value)
#define _MLIMIT 128
#define _MINDEX(m,ckey) ((m)->maxentries < _MLIMIT ? (int)((signed char*)(m)->cells)[ckey] : ((int*)(m)->cells)[ckey])
#define _MNEXT(m,ckey) ((m)->maxentries < _MLIMIT ? (int)((signed char*)(m)->nexts)[ckey] : ((int*)(m)->nexts)[ckey])
#ifdef _MNO_EXPORTS
#define _MSTATIC
#else
#define _MSTATIC static
#endif

typedef struct {
void *cells;
void *nexts;
t_entry *entries;
t_value *values;
hl_free_list lfree;
int ncells;
int *psl;
int nbuckets;
int nentries;
int maxentries;
} t_map;
Expand All @@ -37,52 +32,66 @@ t_map *_MNAME(alloc)() {
}

_MSTATIC _MVAL_TYPE *_MNAME(find)( t_map *m, t_key key ) {
int c, ckey;
int c, dc = 0;
unsigned int hash;

if( !m->values ) return NULL;
if (m->nbuckets == 0) return NULL;
hash = _MNAME(hash)(key);
ckey = hash % ((unsigned)m->ncells);
c = _MINDEX(m,ckey);
while( c >= 0 ) {
if( _MMATCH(c) )
c = hash % ((unsigned)m->nbuckets);
while( m->psl[c] >= 0 && dc <= m->psl[c] ) {
if (_MMATCH(c))
return &m->values[c].value;
c = _MNEXT(m,c);
dc++;
c = (c + 1) % m->nbuckets;
}
return NULL;
}

static void _MNAME(resize)( t_map *m );

_MSTATIC void _MNAME(set_impl)( t_map *m, t_key key, _MVAL_TYPE value ) {
int c, ckey = 0;
int c = -1, prev = -1, dc = 0;
unsigned int hash = _MNAME(hash)(key);
if( m->values ) {
ckey = hash % ((unsigned)m->ncells);
c = _MINDEX(m,ckey);
while( c >= 0 ) {
if( _MMATCH(c) ) {
if( m->nbuckets > 0 ) {
c = hash % ((unsigned)m->nbuckets);
while (m->psl[c] >= 0 && dc <= m->psl[c]) {
if (_MMATCH(c)) {
m->values[c].value = value;
return;
}
c = _MNEXT(m,c);
dc++;
prev = c;
c = (c + 1) % m->nbuckets;
}
if (prev > 0) {
dc--;
c = prev;
}
}
c = hl_freelist_get(&m->lfree);
if( c < 0 ) {
if (m->nentries >= m->maxentries) {
_MNAME(resize)(m);
ckey = hash % ((unsigned)m->ncells);
c = hl_freelist_get(&m->lfree);
dc = 0;
c = hash % ((unsigned)m->nbuckets);
}
_MSET(c);
if( m->maxentries < _MLIMIT ) {
((signed char*)m->nexts)[c] = ((signed char*)m->cells)[ckey];
((signed char*)m->cells)[ckey] = (signed char)c;
} else {
((int*)m->nexts)[c] = ((int*)m->cells)[ckey];
((int*)m->cells)[ckey] = c;
while (m->psl[c] >= 0) {
if (dc > m->psl[c]) {
t_key key_tmp = _MKEY(m, c);
_MVAL_TYPE value_tmp = m->values[c].value;
int dc_tmp = m->psl[c];
_MSET(c);
m->values[c].value = value;
m->psl[c] = dc;
key = key_tmp;
value = value_tmp;
dc = dc_tmp;
hash = _MNAME(hash)(key);
}
dc++;
c = (c + 1) % m->nbuckets;
}
_MSET(c);
m->values[c].value = value;
m->psl[c] = dc;
m->nentries++;
}

Expand All @@ -93,42 +102,20 @@ static void _MNAME(resize)( t_map *m ) {
if( m->nentries != m->maxentries ) hl_error("assert");

// resize
int i = 0;
int nentries = m->maxentries ? ((m->maxentries * 3) + 1) >> 1 : H_SIZE_INIT;
int ncells = nentries >> 2;

while( H_PRIMES[i] < ncells ) i++;
ncells = H_PRIMES[i];

int ksize = nentries < _MLIMIT ? 1 : sizeof(int);
m->entries = (t_entry*)hl_gc_alloc_noptr(nentries * sizeof(t_entry));
m->values = (t_value*)hl_gc_alloc_raw(nentries * sizeof(t_value));
m->maxentries = nentries;

if( old.ncells == ncells && (nentries < _MLIMIT || old.maxentries >= _MLIMIT) ) {
// simply expand
m->nexts = hl_gc_alloc_noptr(nentries * ksize);
memcpy(m->entries,old.entries,old.maxentries * sizeof(t_entry));
memcpy(m->values,old.values,old.maxentries * sizeof(t_value));
memcpy(m->nexts,old.nexts,old.maxentries * ksize);
memset(m->values + old.maxentries, 0, (nentries - old.maxentries) * sizeof(t_value));
hl_freelist_add_range(&m->lfree,old.maxentries,m->maxentries - old.maxentries);
} else {
// expand and remap
m->cells = hl_gc_alloc_noptr((ncells + nentries) * ksize);
m->nexts = (signed char*)m->cells + ncells * ksize;
m->ncells = ncells;
m->nentries = 0;
memset(m->cells,0xFF,ncells * ksize);
memset(m->values, 0, nentries * sizeof(t_value));
hl_freelist_init(&m->lfree);
hl_freelist_add_range(&m->lfree,0,m->maxentries);
for(i=0;i<old.ncells;i++) {
int c = old.maxentries < _MLIMIT ? ((signed char*)old.cells)[i] : ((int*)old.cells)[i];
while( c >= 0 ) {
_MNAME(set_impl)(m,_MKEY((&old),c),old.values[c].value);
c = _MNEXT(&old,c);
}
int nbuckets = old.nbuckets ? old.nbuckets << 1 : H_SIZE_INIT;
m->entries = (t_entry *)hl_gc_alloc_noptr(nbuckets * sizeof(t_entry));
m->values = (t_value *)hl_gc_alloc_raw(nbuckets * sizeof(t_value));
m->psl = (int *)hl_gc_alloc_noptr(nbuckets * sizeof(int));
m->nbuckets = nbuckets;
m->maxentries = nbuckets * 11 >> 4;

// remap
m->nentries = 0;
memset(m->values, 0, nbuckets * sizeof(t_value));
memset(m->psl, -1, nbuckets * sizeof(int));
for (int c = 0; c < old.nbuckets; c++) {
if (old.psl[c] >= 0) {
_MNAME(set_impl)(m, _MKEY((&old), c), old.values[c].value);
}
}
}
Expand All @@ -150,63 +137,59 @@ HL_PRIM vdynamic* _MNAME(get)( t_map *m, t_key key ) {
}

HL_PRIM bool _MNAME(remove)( t_map *m, t_key key ) {
int c, prev = -1, ckey;
int c, dc = 0;
unsigned int hash;
if( !m->cells ) return false;
if( m->nentries == 0 ) return false;
key = _MNAME(filter)(key);
hash = _MNAME(hash)(key);
ckey = hash % ((unsigned)m->ncells);
c = _MINDEX(m,ckey);
while( c >= 0 ) {
if( _MMATCH(c) ) {
hl_freelist_add(&m->lfree,c);
c = hash % ((unsigned)m->nbuckets);
while (m->psl[c] >= 0 && dc <= m->psl[c]) {
if (_MMATCH(c)) {
m->nentries--;
_MERASE(c);
m->values[c].value = NULL;
if( m->maxentries < _MLIMIT ) {
if( prev >= 0 )
((signed char*)m->nexts)[prev] = ((signed char*)m->nexts)[c];
else
((signed char*)m->cells)[ckey] = ((signed char*)m->nexts)[c];
} else {
if( prev >= 0 )
((int*)m->nexts)[prev] = ((int*)m->nexts)[c];
else
((int*)m->cells)[ckey] = ((int*)m->nexts)[c];
m->psl[c] = -1;
// Move all following elements
int next = (c + 1) % m->nbuckets;
while (m->psl[next] > 0) {
key = _MKEY(m, next);
hash = _MNAME(hash)(key);
_MSET(c);
_MERASE(next);
m->values[c].value = m->values[next].value;
m->values[next].value = NULL;
m->psl[c] = m->psl[next] - 1;
m->psl[next] = -1;
c = next;
next = (c + 1) % m->nbuckets;
}
return true;
}
prev = c;
c = _MNEXT(m,c);
dc++;
c = (c + 1) % m->nbuckets;
}
return false;
}

HL_PRIM varray* _MNAME(keys)( t_map *m ) {
varray *a = hl_alloc_array(&hlt_key,m->nentries);
t_key *keys = hl_aptr(a,t_key);
varray *a = hl_alloc_array(&hlt_key, m->nentries);
t_key *keys = hl_aptr(a, t_key);
int p = 0;
int i;
for(i=0;i<m->ncells;i++) {
int c = _MINDEX(m,i);
while( c >= 0 ) {
keys[p++] = _MKEY(m,c);
c = _MNEXT(m,c);
for (int c = 0; c < m->nbuckets; c++) {
if (m->psl[c] >= 0) {
keys[p++] = _MKEY(m, c);
}
}
return a;
}

HL_PRIM varray* _MNAME(values)( t_map *m ) {
varray *a = hl_alloc_array(&hlt_dyn,m->nentries);
vdynamic **values = hl_aptr(a,vdynamic*);
varray *a = hl_alloc_array(&hlt_dyn, m->nentries);
vdynamic **values = hl_aptr(a, vdynamic*);
int p = 0;
int i;
for(i=0;i<m->ncells;i++) {
int c = _MINDEX(m,i);
while( c >= 0 ) {
for (int c = 0; c < m->nbuckets; c++) {
if (m->psl[c] >= 0) {
values[p++] = m->values[c].value;
c = _MNEXT(m,c);
}
}
return a;
Expand Down