PascalHashTable* PHNew(OPHeap* heap, uint64_t num_objects, double load, size_t key_inline_size, size_t valsize) { PascalHashTable* table; uint64_t capacity; uint32_t capacity_clz, capacity_ms4b, capacity_msb; size_t bucket_size; void* bucket_ptr; op_assert(load > 0.0 && load < 1.0, "load %lf must within close interval (0.0, 1.0)\n", load); capacity = (uint64_t)(num_objects / load); if (capacity < 8) capacity = 8; capacity_clz = __builtin_clzl(capacity); capacity_msb = 64 - capacity_clz; capacity_ms4b = round_up_div(capacity, 1UL << (capacity_msb - 4)); capacity = (uint64_t)capacity_ms4b << (capacity_msb - 4); bucket_size = sizeof(oplenref_t) + key_inline_size + valsize; table = OPCalloc(heap, 1, sizeof(PascalHashTable)); if (!table) return NULL; bucket_ptr = OPCalloc(heap, 1, bucket_size * capacity); if (!bucket_ptr) { OPDealloc(table); return NULL; } table->bucket_ref = OPPtr2Ref(bucket_ptr); table->large_data_threshold = DEFAULT_LARGE_DATA_THRESHOLD; table->capacity_clz = capacity_clz; table->capacity_ms4b = capacity_ms4b; table->objcnt_high = (uint64_t)(capacity * load); table->objcnt_low = capacity * 2 / 10; table->key_inline_size = key_inline_size; table->valsize = valsize; return table; }
OPHeap* heap; // Initialize a 64GB OPIC heap via mmap OPHeapNew(&heap); // pointer for accessing the object int *a_ptr = OPMalloc(heap, sizeof(int)); // deallocate an object does not require specifying the heap OPDealloc(a_ptr); void OPHeapWrite(OPHeap* heap, FILE* stream); bool OPHeapRead(OPHeap** heap_ref, FILE* stream); // Convert the pointer to a offset to the OPHeap base address // The pointer must be a pointer created by OPHeap opref_t a_ref = OPPtr2Ref(a_ptr); // Add the offset a_ref with OPHeap base address to restore // the pointer. int* a_ptr = OPRef2Ptr(heap, a_ref); ** * @relates OPHeap * @brief Store a pointer to a root pointer slot in OPHeap. * * @param heap OPHeap instance. * @param ptr the pointer we want to store in root pointer slot. * @param pos index in the root pointer slot. 0 <= pos < 8. */ void OPHeapStorePtr(OPHeap* heap, void* ptr, int pos); /** * @relates OPHeap * @brief Restore a pointer from specified root pointer slot.
static bool PHSizeDown(PascalHashTable* table, OPHash hasher) { const size_t key_inline_size = table->key_inline_size; const size_t valsize = table->valsize; const size_t bucket_size = sizeof(oplenref_t) + key_inline_size + valsize; uint8_t* old_buckets; uint8_t* new_buckets; uint8_t new_capacity_ms4b, new_capacity_clz; uint64_t old_capacity, new_capacity; oplenref_t *recref; bool resized; old_capacity = PHCapacity(table); old_buckets = OPRef2Ptr(table, table->bucket_ref); op_assert(old_capacity > 16, "Can not resize smaller than 16, but got old_capacity %" PRIu64 "\n", old_capacity); switch(table->capacity_ms4b) { case 8: // new load 0.45 case 9: // new load 0.50 case 10: // new load 0.55 case 11: // new load 0.60 new_capacity_ms4b = 8; new_capacity_clz = table->capacity_clz + 1; break; case 12: // new load 0.40 case 13: // new load 0.43 case 14: // new load 0.46 case 15: // new load 0.50 new_capacity_ms4b = 12; new_capacity_clz = table->capacity_clz + 1; break; default: op_assert(false, "Unknown capacity_ms4b %d\n", table->capacity_ms4b); } new_capacity = PHCapacityInternal(new_capacity_clz, new_capacity_ms4b); OP_LOG_INFO(logger, "Resize from %" PRIu64 " to %" PRIu64, old_capacity, new_capacity); new_buckets = OPCalloc(ObtainOPHeap(table), 1, bucket_size * new_capacity); if (!new_buckets) { OP_LOG_ERROR(logger, "Cannot obtain new bucket for size %" PRIu64, new_capacity); return false; } table->objcnt = 0; table->objcnt_high = new_capacity * 8 / 10; table->objcnt_low = new_capacity * 2 / 10; table->capacity_clz = new_capacity_clz; table->capacity_ms4b = new_capacity_ms4b; table->longest_probes = 0; memset(table->stats, 0x00, sizeof(uint32_t) * PROBE_STATS_SIZE); table->bucket_ref = OPPtr2Ref(new_buckets); for (uint64_t idx = 0; idx < old_capacity; idx++) { recref = (oplenref_t*)&old_buckets[idx * bucket_size]; if (!OPLenRefIsEmpty(*recref) && !OPLenRefIsDeleted(*recref)) { PHUpsertPushDown(table, hasher, &old_buckets[idx * bucket_size], 0, NULL, &resized); } } OPDealloc(old_buckets); return true; }
static bool PHSizeUp(PascalHashTable* table, OPHash hasher) { const size_t key_inline_size = table->key_inline_size; const size_t valsize = table->valsize; const size_t bucket_size = sizeof(oplenref_t) + key_inline_size + valsize; const size_t large_data_threshold = table->large_data_threshold; uint8_t* old_buckets; uint8_t* new_buckets; oplenref_t *recref; bool resized; uint8_t new_capacity_ms4b, new_capacity_clz; uint64_t old_capacity, new_capacity; old_capacity = PHCapacity(table); old_buckets = OPRef2Ptr(table, table->bucket_ref); if (old_capacity * bucket_size >= large_data_threshold) { // increase size by 20% ~ 33% switch(table->capacity_ms4b) { case 8: new_capacity_ms4b = 10; new_capacity_clz = table->capacity_clz; break; case 9: case 10: new_capacity_ms4b = 12; new_capacity_clz = table->capacity_clz; break; case 11: case 12: new_capacity_ms4b = 14; new_capacity_clz = table->capacity_clz; break; case 13: case 14: new_capacity_ms4b = 8; new_capacity_clz = table->capacity_clz - 1; break; case 15: new_capacity_ms4b = 10; new_capacity_clz = table->capacity_clz - 1; break; default: op_assert(false, "Unknown capacity_ms4b %d\n", table->capacity_ms4b); } } else { new_capacity_ms4b = 8; new_capacity_clz = table->capacity_ms4b == 8 ? table->capacity_clz - 1 : table->capacity_clz - 2; } new_capacity = PHCapacityInternal(new_capacity_clz, new_capacity_ms4b); OP_LOG_INFO(logger, "Resize from %" PRIu64 " to %" PRIu64, old_capacity, new_capacity); new_buckets = OPCalloc(ObtainOPHeap(table), 1, bucket_size * new_capacity); if (!new_buckets) { OP_LOG_ERROR(logger, "Cannot obtain new bucket for size %" PRIu64, new_capacity); return false; } table->objcnt = 0; table->objcnt_high = new_capacity * 8 / 10; table->objcnt_low = new_capacity * 2 / 10; table->capacity_clz = new_capacity_clz; table->capacity_ms4b = new_capacity_ms4b; table->longest_probes = 0; memset(table->stats, 0x00, sizeof(uint32_t) * PROBE_STATS_SIZE); table->bucket_ref = OPPtr2Ref(new_buckets); for (uint64_t idx = 0; idx < old_capacity; idx++) { recref = (oplenref_t*)&old_buckets[idx * bucket_size]; if (!OPLenRefIsEmpty(*recref) && !OPLenRefIsDeleted(*recref)) { PHUpsertPushDown(table, hasher, &old_buckets[idx * bucket_size], 0, NULL, &resized); } } OPDealloc(old_buckets); return true; }