You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
4.7 KiB
170 lines
4.7 KiB
/*
|
|
* Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <sylvan.h>
|
|
#include <sylvan_sl.h>
|
|
|
|
#include <sys/mman.h> // for mmap, munmap, etc
|
|
|
|
/* A SL_DEPTH of 6 means 32 bytes per bucket, of 14 means 64 bytes per bucket.
|
|
However, there is a very large performance drop with only 6 levels. */
|
|
#define SL_DEPTH 14
|
|
|
|
typedef struct
|
|
{
|
|
BDD dd;
|
|
uint32_t next[SL_DEPTH];
|
|
} sl_bucket;
|
|
|
|
struct sylvan_skiplist
|
|
{
|
|
sl_bucket *buckets;
|
|
size_t size;
|
|
size_t next;
|
|
};
|
|
|
|
#ifndef cas
|
|
#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new)))
|
|
#endif
|
|
|
|
sylvan_skiplist_t
|
|
sylvan_skiplist_alloc(size_t size)
|
|
{
|
|
if (size >= 0x80000000) {
|
|
fprintf(stderr, "sylvan: Trying to allocate a skiplist >= 0x80000000 buckets!\n");
|
|
exit(1);
|
|
}
|
|
sylvan_skiplist_t l = malloc(sizeof(struct sylvan_skiplist));
|
|
l->buckets = (sl_bucket*)mmap(0, sizeof(sl_bucket)*size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0);
|
|
if (l->buckets == (sl_bucket*)-1) {
|
|
fprintf(stderr, "sylvan: Unable to allocate virtual memory (%'zu bytes) for the skiplist!\n", size*sizeof(sl_bucket));
|
|
exit(1);
|
|
}
|
|
l->size = size;
|
|
l->next = 1;
|
|
return l;
|
|
}
|
|
|
|
void
|
|
sylvan_skiplist_free(sylvan_skiplist_t l)
|
|
{
|
|
munmap(l->buckets, sizeof(sl_bucket)*l->size);
|
|
free(l);
|
|
}
|
|
|
|
/**
|
|
* Return the assigned number of the given dd,
|
|
* or 0 if not found.
|
|
*/
|
|
uint64_t
|
|
sylvan_skiplist_get(sylvan_skiplist_t l, MTBDD dd)
|
|
{
|
|
if (dd == mtbdd_false || dd == mtbdd_true) return 0;
|
|
|
|
uint32_t loc = 0, k = SL_DEPTH-1;
|
|
for (;;) {
|
|
/* invariant: [loc].dd < dd */
|
|
/* note: this is always true for loc==0 */
|
|
sl_bucket *e = l->buckets + loc;
|
|
uint32_t loc_next = (*(volatile uint32_t*)&e->next[k]) & 0x7fffffff;
|
|
if (loc_next != 0 && l->buckets[loc_next].dd == dd) {
|
|
/* found */
|
|
return loc_next;
|
|
} else if (loc_next != 0 && l->buckets[loc_next].dd < dd) {
|
|
/* go right */
|
|
loc = loc_next;
|
|
} else if (k > 0) {
|
|
/* go down */
|
|
k--;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID_TASK_IMPL_2(sylvan_skiplist_assign_next, sylvan_skiplist_t, l, MTBDD, dd)
|
|
{
|
|
if (dd == mtbdd_false || dd == mtbdd_true) return;
|
|
|
|
uint32_t trace[SL_DEPTH];
|
|
uint32_t loc = 0, loc_next = 0, k = SL_DEPTH-1;
|
|
for (;;) {
|
|
/* invariant: [loc].dd < dd */
|
|
/* note: this is always true for loc==0 */
|
|
sl_bucket *e = l->buckets + loc;
|
|
loc_next = (*(volatile uint32_t*)&e->next[k]) & 0x7fffffff;
|
|
if (loc_next != 0 && l->buckets[loc_next].dd == dd) {
|
|
/* found */
|
|
return;
|
|
} else if (loc_next != 0 && l->buckets[loc_next].dd < dd) {
|
|
/* go right */
|
|
loc = loc_next;
|
|
} else if (k > 0) {
|
|
/* go down */
|
|
trace[k] = loc;
|
|
k--;
|
|
} else if (!(e->next[0] & 0x80000000) && cas(&e->next[0], loc_next, loc_next|0x80000000)) {
|
|
/* locked */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* claim next item */
|
|
const uint64_t next = __sync_fetch_and_add(&l->next, 1);
|
|
if (next >= l->size) {
|
|
fprintf(stderr, "Out of cheese exception, no more blocks available\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* fill next item */
|
|
sl_bucket *a = l->buckets + next;
|
|
a->dd = dd;
|
|
a->next[0] = loc_next;
|
|
compiler_barrier();
|
|
l->buckets[loc].next[0] = next;
|
|
|
|
/* determine height */
|
|
uint64_t h = 1 + __builtin_clz(LACE_TRNG) / 2;
|
|
if (h > SL_DEPTH) h = SL_DEPTH;
|
|
|
|
/* go up and create links */
|
|
for (k=1;k<h;k++) {
|
|
loc = trace[k];
|
|
for (;;) {
|
|
sl_bucket *e = l->buckets + loc;
|
|
/* note, at k>0, no locks on edges */
|
|
uint32_t loc_next = *(volatile uint32_t*)&e->next[k];
|
|
if (loc_next != 0 && l->buckets[loc_next].dd < dd) {
|
|
loc = loc_next;
|
|
} else {
|
|
a->next[k] = loc_next;
|
|
if (cas(&e->next[k], loc_next, next)) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t
|
|
sylvan_skiplist_count(sylvan_skiplist_t l)
|
|
{
|
|
return l->next - 1;
|
|
}
|
|
|
|
MTBDD
|
|
sylvan_skiplist_getr(sylvan_skiplist_t l, uint64_t index)
|
|
{
|
|
return l->buckets[index].dd;
|
|
}
|