From c0083c580193aa64cc385ec9253decc37030233a Mon Sep 17 00:00:00 2001 From: Unbit Date: Sun, 10 Feb 2013 12:11:47 +0100 Subject: [PATCH] first commit for bitmap based cache --- core/cache.c | 175 ++++++++++++++++++++++++++++++++++++++++++--------- uwsgi.h | 125 ++++++++++++++++++------------------ 2 files changed, 211 insertions(+), 89 deletions(-) diff --git a/core/cache.c b/core/cache.c index efdbad41..c711d7a7 100644 --- a/core/cache.c +++ b/core/cache.c @@ -4,17 +4,102 @@ extern struct uwsgi_server uwsgi; #define cache_item(x) (struct uwsgi_cache_item *) (((char *)uc->items) + ((sizeof(struct uwsgi_cache_item)+uc->keysize) * x)) // block bitmap manager -/* -static uint64_t cache_get_block_by_bitmap(struct uwsgi_cache *uc, uint64_t len) { - // first of all, how many blocks i need ? - uint64_t blocks = len/uc->blocksize; - if (len%uc->blocksize > 0) blocks++; - uwsgi_log("searching for %d free blocks\n"); - return 0; -} + +/* how the cache botmap works: + + a bitmap is a shared mempry area allocated when requested by the user with --cache2 + + Each block maps to a bit in the bitmap. If the corresponding bit is cleared + the block is usable otherwise the block scanner will search for the next one. + + Object can be placed only on consecutive blocks, fragmentation is not allowed. + + To increase the scan performance, a 64bit pointer to the last used bit + 1 is hold + + To search for free blocks you run + + uint64_t uwsgi_cache_find_free_block(struct uwsgi_cache *uc, size_t need) + + where need is the size of the object + */ -static void cache_unmark_blocks(struct uwsgi_cache *uc, uint64_t index, uint64_t len) { +static uint64_t uwsgi_cache_find_free_blocks(struct uwsgi_cache *uc, uint64_t need) { + // how many blocks we need ? + uint64_t needed_blocks = need/uc->blocksize; + if (need % uc->blocksize > 0) needed_blocks++; + + // which is the first free bit? + uint64_t bitmap_byte = 0; + uint8_t bitmap_bit = 0; + + if (uc->blocks_bitmap_pos > 0) { + bitmap_byte = uc->blocks_bitmap_pos/8; + bitmap_bit = uc->blocks_bitmap_pos % 8; + } + + // ok we now have the start position, let's search for contigous blocks + uint8_t *bitmap = uc->blocks_bitmap; + uint64_t base = 0xffffffffffffffff; + uint8_t base_bit = 0; + uint64_t j; + uint64_t found = 0; + uint64_t need_to_scan = uc->blocks_bitmap_size; + j = bitmap_byte; + uwsgi_log("start scanning %llu bytes starting from %llu need: %llu\n", need_to_scan, bitmap_byte, needed_blocks); + while(need_to_scan) { + uint8_t num = bitmap[j]; + uint8_t i; + uint8_t bit_pos = 0; + if (j == bitmap_byte) { + i = 1 << (7-bitmap_bit); + bit_pos = bitmap_bit; + } + else { + i = 1 <<7; + } + while(i > 0) { + // used block + if (num & i) { + found = 0; + base = 0xffffffffffffffff; + base_bit = 0; + } + // free block + else { + if (base == 0xffffffffffffffff ) { + base = j; + base_bit = bit_pos; + } + found++; + if (found == needed_blocks) { + printf("found %llu consecutive bit starting from byte %llu\n", found, base); + uwsgi_log("FOUND %llu %u %llu\n", base, base_bit, (base*8) + base_bit); + return ((base*8) + base_bit); + } + } + i >>= 1; + bit_pos++; + } + j++; + need_to_scan--; + } + + + // no more free blocks + return 0xffffffffffffffff; +} + +static uint64_t cache_mark_blocks(struct uwsgi_cache *uc, uint64_t index, uint64_t len) { + uint64_t needed_blocks = len/8; + if (len % 8 > 0) needed_blocks++; + return needed_blocks; +} + +static uint64_t cache_unmark_blocks(struct uwsgi_cache *uc, uint64_t index, uint64_t len) { + uint64_t needed_blocks = len/8; + if (len % 8 > 0) needed_blocks++; + return needed_blocks; /* uint64_t base = index/8; uint64_t bit = index%8; @@ -72,7 +157,10 @@ void uwsgi_cache_init(struct uwsgi_cache *uc) { uc->filesize = ( (sizeof(struct uwsgi_cache_item)+uc->keysize) * uc->max_items) + (uc->blocksize * uc->blocks); if (uc->use_blocks_bitmap) { - uc->blocks_bitmap = uwsgi_calloc_shared(uc->blocks/8); + uwsgi_log("BLOCKS BITMAP\n"); + uc->blocks_bitmap_size = uc->blocks/8; + if (uc->blocks % 8 > 0) uc->blocks_bitmap_size++; + uc->blocks_bitmap = uwsgi_calloc_shared(uc->blocks_bitmap_size); } //uwsgi.cache_items = (struct uwsgi_cache_item *) mmap(NULL, sizeof(struct uwsgi_cache_item) * uwsgi.cache_max_items, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); @@ -137,11 +225,12 @@ void uwsgi_cache_init(struct uwsgi_cache *uc) { uc->lock = uwsgi_rwlock_init("cache"); } - uwsgi_log("*** Cache \"%s\" initialized: %lluMB (key: %llu bytes, keys: %llu bytes, data: %llu bytes) preallocated ***\n", + uwsgi_log("*** Cache \"%s\" initialized: %lluMB (key: %llu bytes, keys: %llu bytes, data: %llu bytes, bitmap: %llu bytes) preallocated ***\n", uc->name, (unsigned long long) uc->filesize / (1024 * 1024), (unsigned long long) sizeof(struct uwsgi_cache_item)+uc->keysize, - (unsigned long long) ((sizeof(struct uwsgi_cache_item)+uc->keysize) * uc->max_items), (unsigned long long) (uc->blocksize * uc->max_items)); + (unsigned long long) ((sizeof(struct uwsgi_cache_item)+uc->keysize) * uc->max_items), (unsigned long long) (uc->blocksize * uc->max_items), + (unsigned long long) uc->blocks_bitmap_size); struct uwsgi_string_list *usl = uc->nodes; @@ -225,7 +314,7 @@ next: uwsgi_cache_load_files(uc); } -static inline uint64_t uwsgi_cache_get_index(struct uwsgi_cache *uc, char *key, uint16_t keylen) { +static uint64_t uwsgi_cache_get_index(struct uwsgi_cache *uc, char *key, uint16_t keylen) { uint32_t hash = uc->hash->func(key, keylen); @@ -292,7 +381,7 @@ char *uwsgi_cache_get2(struct uwsgi_cache *uc, char *key, uint16_t keylen, uint6 *valsize = uci->valsize; uci->hits++; uc->hits++; - return uc->data + (index * uc->blocksize); + return uc->data + (uci->first_block * uc->blocksize); } uc->miss++; @@ -311,12 +400,16 @@ int uwsgi_cache_del2(struct uwsgi_cache *uc, char *key, uint16_t keylen, uint64_ if (index) { uci = cache_item(index); uci->keysize = 0; - cache_unmark_blocks(uc, index, uci->valsize); uci->valsize = 0; - // try to return to initial condition... - if (index == uc->first_available_block - 1) { - uc->first_available_block--; - //uwsgi_log("FACI: %llu STACK PTR: %llu\n", (unsigned long long) uwsgi.shared->cache_first_available_block, (unsigned long long) uwsgi.shared->cache_unused_blocks_stack_ptr); + if (!uc->blocks_bitmap) { + // try to return to initial condition... + if (index == uc->first_available_block - 1) { + uc->first_available_block--; + //uwsgi_log("FACI: %llu STACK PTR: %llu\n", (unsigned long long) uwsgi.shared->cache_first_available_block, (unsigned long long) uwsgi.shared->cache_unused_blocks_stack_ptr); + } + } + else { + cache_unmark_blocks(uc, uci->first_block, uci->valsize); } ret = 0; // relink collisioned entry @@ -394,17 +487,16 @@ int uwsgi_cache_set2(struct uwsgi_cache *uc, char *key, uint16_t keylen, char *v if (keylen > uc->keysize) return -1; - if (vallen > uc->blocksize) return -1; - - if (uc->first_available_block >= uc->max_items && !uc->unused_blocks_stack_ptr) { - uwsgi_log("*** DANGER cache is FULL !!! ***\n"); - uc->full++; - goto end; - } + if (!uc->blocks_bitmap && vallen > uc->blocksize) return -1; //uwsgi_log("putting cache data in key %.*s %d\n", keylen, key, vallen); index = uwsgi_cache_get_index(uc, key, keylen); if (!index) { + if (uc->first_available_block >= uc->max_items && !uc->unused_blocks_stack_ptr) { + uwsgi_log("*** DANGER cache \"%s\" is FULL !!! ***\n", uc->name); + uc->full++; + goto end; + } if (uc->unused_blocks_stack_ptr) { //uwsgi_log("!!! REUSING CACHE SLOT !!! (faci: %llu)\n", (unsigned long long) uwsgi.shared->cache_first_available_block); index = uc->unused_blocks_stack[uc->unused_blocks_stack_ptr]; @@ -416,7 +508,28 @@ int uwsgi_cache_set2(struct uwsgi_cache *uc, char *key, uint16_t keylen, char *v uc->first_available_block++; } } + uci = cache_item(index); + if (!uc->blocks_bitmap) { + uci->first_block = index; + } + else { + uci->first_block = uwsgi_cache_find_free_blocks(uc, vallen); + if (uci->first_block == 0xffffffffffffffff) { + uwsgi_log("*** DANGER cache \"%s\" is FULL !!! ***\n", uc->name); + uc->full++; + goto end; + } + // mark used blocks; + uint64_t needed_blocks = cache_mark_blocks(uc, uci->first_block, vallen); + // optimize teh scan + if (uc->blocks_bitmap_pos + (needed_blocks+1) > uc->blocks) { + uc->blocks_bitmap_pos = 0; + } + else { + uc->blocks_bitmap_pos += needed_blocks; + } + } if (expires && !(flags & UWSGI_CACHE_FLAG_ABSEXPIRE)) expires += uwsgi_now(); uci->expires = expires; @@ -424,7 +537,7 @@ int uwsgi_cache_set2(struct uwsgi_cache *uc, char *key, uint16_t keylen, char *v uci->hits = 0; uci->flags = flags; memcpy(uci->key, key, keylen); - memcpy(((char *) uc->data) + (index * uc->blocksize), val, vallen); + memcpy(((char *) uc->data) + (uci->first_block * uc->blocksize), val, vallen); // set this as late as possibile (to reduce races risk) @@ -460,7 +573,10 @@ int uwsgi_cache_set2(struct uwsgi_cache *uc, char *key, uint16_t keylen, char *v expires += uwsgi_now(); uci->expires = expires; } - memcpy(uc->data + (index * uc->blocksize), val, vallen); + if (uc->blocks_bitmap) { + // we have a special case here, as we need to find a new series of free blocks + } + memcpy(uc->data + (uci->first_block * uc->blocksize), val, vallen); uci->valsize = vallen; ret = 0; } @@ -894,6 +1010,7 @@ struct uwsgi_cache *uwsgi_cache_create(char *arg) { char *c_nodes = NULL; char *c_sync = NULL; char *c_udp_servers = NULL; + char *c_bitmap = NULL; if (uwsgi_kvlist_parse(arg, strlen(arg), ',', '=', "name", &c_name, @@ -918,6 +1035,7 @@ struct uwsgi_cache *uwsgi_cache_create(char *arg) { "udp_server", &c_udp_servers, "udpservers", &c_udp_servers, "udpserver", &c_udp_servers, + "bitmap", &c_bitmap, NULL)) { uwsgi_log("unable to parse cache definition\n"); exit(1); @@ -956,6 +1074,7 @@ struct uwsgi_cache *uwsgi_cache_create(char *arg) { if (!uc->hashsize) { uwsgi_log("invalid cache hashsize for \"%s\"\n", uc->name); exit(1); } if (c_keysize) uc->keysize = uwsgi_n64(c_keysize); if (!uc->keysize) { uwsgi_log("invalid cache keysize for \"%s\"\n", uc->name); exit(1); } + if (c_bitmap) uc->use_blocks_bitmap = 1; uc->store_sync = uwsgi.cache_store_sync; if (c_store_sync) { uc->store_sync = uwsgi_n64(c_store_sync); } diff --git a/uwsgi.h b/uwsgi.h index 20a7b9eb..f6c5a008 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -321,8 +321,6 @@ extern "C" { #define UWSGI_CACHE_FLAG_LOCAL 0x04 #define UWSGI_CACHE_FLAG_ABSEXPIRE 0x08 -#define uwsgi_cache_update_start(x, y, z) uwsgi_cache_set(x, y, "", 0, CACHE_FLAG_UNGETTABLE) - #ifdef UWSGI_SSL #include "openssl/conf.h" #include "openssl/ssl.h" @@ -330,53 +328,53 @@ extern "C" { #endif - struct uwsgi_buffer { - char *buf; - size_t pos; - size_t len; - size_t limit; - }; +struct uwsgi_buffer { + char *buf; + size_t pos; + size_t len; + size_t limit; +}; - struct uwsgi_string_list { +struct uwsgi_string_list { - char *value; - size_t len; - uint64_t custom; - uint64_t custom2; - void *custom_ptr; - struct uwsgi_string_list *next; - }; + char *value; + size_t len; + uint64_t custom; + uint64_t custom2; + void *custom_ptr; + struct uwsgi_string_list *next; +}; - struct uwsgi_custom_option { +struct uwsgi_custom_option { - char *name; - char *value; - int has_args; - struct uwsgi_custom_option *next; - }; + char *name; + char *value; + int has_args; + struct uwsgi_custom_option *next; +}; - struct uwsgi_lock_item { - char *id; - void *lock_ptr; - int rw; - pid_t pid; - int can_deadlock; - struct uwsgi_lock_item *next; - }; +struct uwsgi_lock_item { + char *id; + void *lock_ptr; + int rw; + pid_t pid; + int can_deadlock; + struct uwsgi_lock_item *next; +}; - struct uwsgi_lock_ops { - struct uwsgi_lock_item *(*lock_init) (char *); - pid_t(*lock_check) (struct uwsgi_lock_item *); - void (*lock) (struct uwsgi_lock_item *); - void (*unlock) (struct uwsgi_lock_item *); +struct uwsgi_lock_ops { + struct uwsgi_lock_item *(*lock_init) (char *); + pid_t(*lock_check) (struct uwsgi_lock_item *); + void (*lock) (struct uwsgi_lock_item *); + void (*unlock) (struct uwsgi_lock_item *); - struct uwsgi_lock_item *(*rwlock_init) (char *); - pid_t(*rwlock_check) (struct uwsgi_lock_item *); - void (*rlock) (struct uwsgi_lock_item *); - void (*wlock) (struct uwsgi_lock_item *); - void (*rwunlock) (struct uwsgi_lock_item *); - }; + struct uwsgi_lock_item *(*rwlock_init) (char *); + pid_t(*rwlock_check) (struct uwsgi_lock_item *); + void (*rlock) (struct uwsgi_lock_item *); + void (*wlock) (struct uwsgi_lock_item *); + void (*rwunlock) (struct uwsgi_lock_item *); +}; #define uwsgi_lock_init(x) uwsgi.lock_ops.lock_init(x) #define uwsgi_lock_check(x) uwsgi.lock_ops.lock_check(x) @@ -642,26 +640,28 @@ extern "C" { void uwsgi_hash_algo_register_all(void); // maintain alignment here !!! - struct uwsgi_cache_item { - // item specific flags - uint64_t flags; - // size of the key - uint64_t keysize; - // hash of the key - uint64_t hash; - // size of the value (64bit) - uint64_t valsize; - // 64bit expiration (0 for immortal) - uint64_t expires; - // 64bit hits - uint64_t hits; - // previous same-hash item - uint64_t prev; - // next same-hash item - uint64_t next; - // key characters follows... - char key[]; - } __attribute__ ((__packed__)); +struct uwsgi_cache_item { + // item specific flags + uint64_t flags; + // size of the key + uint64_t keysize; + // hash of the key + uint64_t hash; + // size of the value (64bit) + uint64_t valsize; + // block position (in non-bitmap mode maps to the key index) + uint64_t first_block; + // 64bit expiration (0 for immortal) + uint64_t expires; + // 64bit hits + uint64_t hits; + // previous same-hash item + uint64_t prev; + // next same-hash item + uint64_t next; + // key characters follows... + char key[]; +} __attribute__ ((__packed__)); struct uwsgi_cache { char *name; @@ -678,8 +678,11 @@ struct uwsgi_cache { uint64_t first_available_block; uint64_t *unused_blocks_stack; uint64_t unused_blocks_stack_ptr; + uint8_t use_blocks_bitmap; uint8_t *blocks_bitmap; + uint64_t blocks_bitmap_pos; + uint64_t blocks_bitmap_size; uint64_t max_items; uint64_t n_items;