void G_COLD map_test(void) { sha1_t *keys; map_t *mh, *mp; int i; size_t count; int tests; struct { unsigned insertion, contains, removal; } faster = { 0, 0, 0}; bool verbose = common_stats > 1; if (common_stats <= 0) return; XMALLOC_ARRAY(keys, ITEM_COUNT); for (i = 0; i < ITEM_COUNT; i++) random_bytes(keys[i].data, SHA1_RAW_SIZE); mh = map_create_hash(sha1_hash, sha1_eq); mp = map_create_patricia(KEYBITS); timeit(test_map_insert, mh, keys, ITEM_COUNT, LOOPS, "map hash insertion", verbose); timeit(test_map_insert, mp, keys, ITEM_COUNT, LOOPS, "map PATRICIA insertion", verbose); map_destroy(mh); map_destroy(mp); for (tests = 0, count = ITEM_COUNT; count > 1; count /= 10) { double htime; double ptime; tests++; mh = map_create_hash(sha1_hash, sha1_eq); mp = map_create_patricia(KEYBITS); htime = timeit(test_map_insert, mh, keys, count, 1, "map hash reloading", verbose); ptime = timeit(test_map_insert, mp, keys, count, 1, "map PATRICIA reloading", verbose); if (verbose) g_info("PATRICIA insertion %s than hash with %zu items", ptime < htime ? "faster" : "slower", count); if (ptime < htime) faster.insertion++; htime = timeit(test_map_contains, mh, keys, count, LOOPS, "map hash contains", verbose); ptime = timeit(test_map_contains, mp, keys, count, LOOPS, "map PATRICIA contains", verbose); if (verbose) g_info("PATRICIA contains %s than hash with %zu items", ptime < htime ? "faster" : "slower", count); if (ptime < htime) faster.contains++; htime = timeit(test_map_remove, mh, keys, count, 1, "map hash remove", verbose); ptime = timeit(test_map_remove, mp, keys, count, 1, "map PATRICIA remove", verbose); if (verbose) g_info("PATRICIA remove %s than hash with %zu items", ptime < htime ? "faster" : "slower", count); if (ptime < htime) faster.removal++; map_destroy(mh); map_destroy(mp); } if (faster.insertion) g_info("PATRICIA insert was faster than hash in %d out of %d tests", faster.insertion, tests); if (faster.contains) g_info( "PATRICIA contains was faster than hash in %d out of %d tests", faster.contains, tests); if (faster.removal) g_info("PATRICIA remove was faster than hash in %d out of %d tests", faster.removal, tests); XFREE_NULL(keys); }
/** * Create a new DBM wrapper over already created DB map. * * If value_data_size is 0, the length for value_size is used. * * @param dm The database (already opened) * @param name Database name, for logs * @param value_size Maximum value size, in bytes (structure) * @param value_data_size Maximum value size, in bytes (serialized form) * @param pack Serialization routine for values * @param unpack Deserialization routine for values * @param valfree Free routine for value (or NULL if none needed) * @param cache_size Amount of items to cache (0 = no cache, 1 = default) * @param hash_func Key hash function * @param eq_func Key equality test function * * If serialization and deserialization routines are NULL pointers, data * will be stored and retrieved as-is. In that case, they must be both * NULL. */ dbmw_t * dbmw_create(dbmap_t *dm, const char *name, size_t value_size, size_t value_data_size, dbmw_serialize_t pack, dbmw_deserialize_t unpack, dbmw_free_t valfree, size_t cache_size, GHashFunc hash_func, GEqualFunc eq_func) { dbmw_t *dw; g_assert(pack == NULL || value_size); g_assert((pack != NULL) == (unpack != NULL)); g_assert(valfree == NULL || unpack != NULL); g_assert(dm); WALLOC0(dw); dw->magic = DBMW_MAGIC; dw->dm = dm; dw->name = name; dw->key_size = dbmap_key_size(dm); dw->key_len = dbmap_key_length(dm); dw->value_size = value_size; dw->value_data_size = 0 == value_data_size ? value_size : value_data_size; /* Make sure we do not violate the SDBM constraint */ g_assert(sdbm_is_storable(dw->key_size, dw->value_data_size)); /* * There must be a serialization routine if the serialized length is not * the same as the structure length. */ g_assert(dw->value_size == dw->value_data_size || pack != NULL); /* * For a small amount of items, a PATRICIA tree is more efficient * than a hash table although it uses more memory. */ if ( NULL == dw->key_len && dw->key_size * 8 <= PATRICIA_MAXBITS && cache_size <= DBMW_CACHE ) { dw->values = map_create_patricia(dw->key_size * 8); } else { dw->values = map_create_hash(hash_func, eq_func); } dw->keys = hash_list_new(hash_func, eq_func); dw->pack = pack; dw->unpack = unpack; dw->valfree = valfree; /* * If a serialization routine is provided, we'll also have a need for * deserialization. Allocate the message in/out streams. * * We're allocating one more byte than necessary to be able to check * whether serialization stays within the imposed boundaries. */ if (dw->pack) { dw->bs = bstr_create(); dw->mb = pmsg_new(PMSG_P_DATA, NULL, dw->value_data_size + 1); } /* * If cache_size is zero, we won't cache anything but the latest * value requested, in deserialized form. If modified, it will be * written back immediately. * * If cache_size is one, use the default (DBMW_CACHE). * * Any other value is used as-is. */ if (0 == cache_size) dw->max_cached = 1; /* No cache, only keep latest around */ else if (cache_size == 1) dw->max_cached = DBMW_CACHE; else dw->max_cached = cache_size; if (common_dbg) g_debug("DBMW created \"%s\" with %s back-end " "(max cached = %lu, key=%lu bytes, value=%lu bytes, " "%lu max serialized)", dw->name, dbmw_map_type(dw) == DBMAP_SDBM ? "sdbm" : "map", (gulong) dw->max_cached, (gulong) dw->key_size, (gulong) dw->value_size, (gulong) dw->value_data_size); return dw; }