Compare commits
No commits in common. "6c1adb16554385aac8541530da37e788ac3266b9" and "047babeb1b1e4258a1f78c25d3a355b1f6c8e17c" have entirely different histories.
6c1adb1655
...
047babeb1b
78
main.cpp
78
main.cpp
@ -1,72 +1,9 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <chrono>
|
|
||||||
#include "amap.h"
|
#include "amap.h"
|
||||||
#include "simap.h"
|
#include "simap.h"
|
||||||
#include "mapmap.hpp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates keys or returns the ith key. Used for performance tests.
|
|
||||||
*
|
|
||||||
* @param i When "create" is false, we return the ith key (does not check OOB)
|
|
||||||
* @param create When true, we initialize the keystore with keys generated from 0..i indices.
|
|
||||||
* @returns The ith key when create==false, otherwise undefined.
|
|
||||||
*/
|
|
||||||
inline const char *keystore(int i, bool create = false) noexcept {
|
|
||||||
static thread_local std::vector<std::string> keys;
|
|
||||||
|
|
||||||
if(!create) {
|
|
||||||
return keys[i].c_str();
|
|
||||||
} else {
|
|
||||||
keys.resize(0);
|
|
||||||
keys.reserve(0);
|
|
||||||
std::string key = "k";
|
|
||||||
for(int j = 0; j < i; ++j) {
|
|
||||||
keys.push_back(key + std::to_string(j));
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates keys or returns the ith key. Used for performance tests.
|
|
||||||
*
|
|
||||||
* @param i When "create" is false, we return the ith data (does not check OOB)
|
|
||||||
* @param create When true, we initialize the datastore with datas generated from 0..i indices.
|
|
||||||
* @returns The ith data when create==false, otherwise undefined.
|
|
||||||
*/
|
|
||||||
inline int *datastore(int i, bool create = false) noexcept {
|
|
||||||
static thread_local std::vector<int> keys;
|
|
||||||
|
|
||||||
if(!create) {
|
|
||||||
return &(keys[i]);
|
|
||||||
} else {
|
|
||||||
keys.resize(0);
|
|
||||||
keys.reserve(0);
|
|
||||||
for(int j = 0; j < i; ++j) {
|
|
||||||
keys.push_back(j);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_perf(amap mapdo, void *map, int max_key, const char *what) {
|
|
||||||
auto begin = std::chrono::high_resolution_clock::now();
|
|
||||||
for(int i = 0; i < max_key; ++i) {
|
|
||||||
const char *key = keystore(i);
|
|
||||||
int *data = datastore(i);
|
|
||||||
mapdo(map, AMAP_SET, key, data);
|
|
||||||
}
|
|
||||||
auto end = std::chrono::high_resolution_clock::now();
|
|
||||||
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
|
|
||||||
|
|
||||||
printf("Insertion time for %d elements (%s): %.3f ms.\n", max_key, what, elapsed.count() * 1e-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_basics(amap mapdo, void *map) {
|
void test_basics(amap mapdo, void *map) {
|
||||||
/* Most basics */
|
|
||||||
assert(NULL == mapdo(map, AMAP_GET, "asdf", NULL));
|
assert(NULL == mapdo(map, AMAP_GET, "asdf", NULL));
|
||||||
int i = 42;
|
int i = 42;
|
||||||
int *iptr;
|
int *iptr;
|
||||||
@ -93,7 +30,7 @@ void test_basics(amap mapdo, void *map) {
|
|||||||
assert(NULL != mapdo(map, AMAP_SET, "meaningless1", &i));
|
assert(NULL != mapdo(map, AMAP_SET, "meaningless1", &i));
|
||||||
assert(NULL != mapdo(map, AMAP_SET, "meaning2", &i));
|
assert(NULL != mapdo(map, AMAP_SET, "meaning2", &i));
|
||||||
const char *helloworld = "Hello world!";
|
const char *helloworld = "Hello world!";
|
||||||
assert(NULL != mapdo(map, AMAP_SET, "hello", (char *)helloworld)); /* ugly cast... */
|
assert(NULL != mapdo(map, AMAP_SET, "hello", (char *)helloworld)); /* TODO: ugly cast... */
|
||||||
|
|
||||||
assert(NULL != (chptr = (const char *)mapdo(map, AMAP_GET, "hello", NULL)));
|
assert(NULL != (chptr = (const char *)mapdo(map, AMAP_GET, "hello", NULL)));
|
||||||
assert(strlen(chptr) == strlen(helloworld));
|
assert(strlen(chptr) == strlen(helloworld));
|
||||||
@ -107,20 +44,9 @@ void test_basics(amap mapdo, void *map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
/* Basic tests */
|
/* test simap */
|
||||||
simap_instance si = simap_create();
|
simap_instance si = simap_create();
|
||||||
test_basics(simap, &si);
|
test_basics(simap, &si);
|
||||||
|
|
||||||
mapmap_instance mi = mapmap_create();
|
|
||||||
test_basics(mapmap, &mi);
|
|
||||||
|
|
||||||
/* Performance tests */
|
|
||||||
int i = 1000;
|
|
||||||
keystore(i, true);
|
|
||||||
datastore(i, true);
|
|
||||||
|
|
||||||
test_perf(simap, &si, i, "simap");
|
|
||||||
test_perf(mapmap, &mi, i, "std::map");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
2
makefile
2
makefile
@ -2,5 +2,3 @@ debug:
|
|||||||
g++ main.cpp -g -Wall -o main
|
g++ main.cpp -g -Wall -o main
|
||||||
release:
|
release:
|
||||||
g++ main.cpp -O2 -Wall -o main
|
g++ main.cpp -O2 -Wall -o main
|
||||||
release-native:
|
|
||||||
g++ main.cpp -march=native -O3 -Wall -o main
|
|
||||||
|
39
mapmap.hpp
39
mapmap.hpp
@ -1,39 +0,0 @@
|
|||||||
#ifndef MAPMAP_HPP
|
|
||||||
#define MAPMAP_HPP
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <cassert>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
struct mapmap_instance {
|
|
||||||
std::map<const char *, void *> m;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline mapmap_instance mapmap_create() {
|
|
||||||
mapmap_instance ret;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void* mapmap(void *amap_instance, AMAP_OP op, const char *key, void *ptr) {
|
|
||||||
mapmap_instance *map = (mapmap_instance *) amap_instance;
|
|
||||||
if(op == AMAP_GET) {
|
|
||||||
try {
|
|
||||||
return map->m[key];
|
|
||||||
} catch(...) {
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
} else if(op == AMAP_SET) {
|
|
||||||
try {
|
|
||||||
map->m[key] = ptr;
|
|
||||||
return map; // non-null
|
|
||||||
} catch(...) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else { // if(op == AMAP_ERASE) {
|
|
||||||
assert(op == AMAP_ERASE);
|
|
||||||
map->m = std::move(std::map<const char *, void *>());
|
|
||||||
return (void *)((uint8_t)(NULL) - 1L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // MAPMAP_HPP
|
|
54
simap.h
54
simap.h
@ -7,24 +7,12 @@
|
|||||||
#include "amap.h"
|
#include "amap.h"
|
||||||
#include "arena.h/arena.h"
|
#include "arena.h/arena.h"
|
||||||
|
|
||||||
/* Perf trickery */
|
/** This is to ensure 8byte storage of pointers (with possible padding) */
|
||||||
|
union simap_ptr64 {
|
||||||
/* I have no idea what MSVC has instead... */
|
void *ptr;
|
||||||
#ifdef _MSC_VER
|
uint64_t u64;
|
||||||
#define SM_THREAD_LOCAL __declspec(thread)
|
};
|
||||||
#define SM_PREFETCH(x)
|
typedef union simap_ptr64 simap_ptr64;
|
||||||
#define SM_LIKELY(x)
|
|
||||||
#define SM_UNLIKELY(x)
|
|
||||||
#define SM_NOINLINE __declspec(noinline)
|
|
||||||
#define SM_ALWAYS_INLINE __forceinline
|
|
||||||
#else
|
|
||||||
#define SM_THREAD_LOCAL __thread
|
|
||||||
#define SM_PREFETCH(x) __builtin_prefetch(x)
|
|
||||||
#define SM_LIKELY(x) __builtin_expect((x),1)
|
|
||||||
#define SM_UNLIKELY(x) __builtin_expect((x),0)
|
|
||||||
#define SM_NOINLINE __attribute__ ((noinline))
|
|
||||||
#define SM_ALWAYS_INLINE __attribute__ ((always_inline))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A "peasantly" map data structure backed by arena.h - basically a toy data structure...
|
* A "peasantly" map data structure backed by arena.h - basically a toy data structure...
|
||||||
@ -76,22 +64,12 @@ static inline simap_instance simap_create() {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void* simap(void *amap_instance, AMAP_OP op, const char *key, void *ptr);
|
|
||||||
|
|
||||||
/** The first 8 characters are stored as uint64_t for fast checks */
|
|
||||||
union simap_c64 {
|
union simap_c64 {
|
||||||
char str8[8];
|
char str8[8];
|
||||||
uint64_t u64;
|
uint64_t u64;
|
||||||
};
|
};
|
||||||
typedef union simap_char64 simap_char64;
|
typedef union simap_char64 simap_char64;
|
||||||
|
|
||||||
/** This is to ensure 8byte storage of pointers (with possible padding) */
|
|
||||||
union simap_ptr64 {
|
|
||||||
void *ptr;
|
|
||||||
uint64_t u64;
|
|
||||||
};
|
|
||||||
typedef union simap_ptr64 simap_ptr64;
|
|
||||||
|
|
||||||
static inline simap_ptr64 *simap_search_internal(simap_instance *map, const char *key) {
|
static inline simap_ptr64 *simap_search_internal(simap_instance *map, const char *key) {
|
||||||
/* Construct prefix (fast-key) */
|
/* Construct prefix (fast-key) */
|
||||||
size_t keylen = strlen(key);
|
size_t keylen = strlen(key);
|
||||||
@ -104,14 +82,13 @@ static inline simap_ptr64 *simap_search_internal(simap_instance *map, const char
|
|||||||
/* Construct keyremains - might point to the \0 terminator only if smallkey or 8 bytes exactly */
|
/* Construct keyremains - might point to the \0 terminator only if smallkey or 8 bytes exactly */
|
||||||
const char *keyremains = key + prefixlen;
|
const char *keyremains = key + prefixlen;
|
||||||
|
|
||||||
/* TODO: Maybe this is buggy when we access behind our own data? */
|
|
||||||
/* TODO: Maybe I should create separate function for fast-lookup returning "next" pointer from a pointer to autovectorize? */
|
/* TODO: Maybe I should create separate function for fast-lookup returning "next" pointer from a pointer to autovectorize? */
|
||||||
/* Lookup prefix (fast-key) - hopefully this gets vectorized (should be)!!! */
|
/* Lookup prefix (fast-key) - hopefully this gets vectorized (should be)!!! */
|
||||||
uint64_t *base = map->base;
|
uint64_t *base = map->base;
|
||||||
uint64_t *tipp = map->base;
|
uint64_t *tipp = map->base;
|
||||||
for(uint32_t i = 0; i < map->usage_end / 8; ++i, ++tipp) {
|
for(uint32_t i = 0; i < map->usage_end / 8; ++i, ++tipp) {
|
||||||
/* Fast lookup */
|
/* Fast lookup */
|
||||||
if((*tipp == prefix.u64)) {
|
if(*tipp == prefix.u64) {
|
||||||
/* First check the remains of the string (only if needed) */
|
/* First check the remains of the string (only if needed) */
|
||||||
if(!is_smallkey) {
|
if(!is_smallkey) {
|
||||||
char *tippremains = (char *)((uint8_t *)tipp + sizeof(uint64_t));
|
char *tippremains = (char *)((uint8_t *)tipp + sizeof(uint64_t));
|
||||||
@ -173,11 +150,11 @@ static inline uint32_t simap_elem_storage_size(const char *key) {
|
|||||||
padding;
|
padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Force-add the (key,value) to the end of the map. Use this if you prefill your map one-by-one and need speed */
|
/** Force-add the (key,value) to the end of the map */
|
||||||
static inline void *simap_force_add(simap_instance *map, const char *key, void *ptr) {
|
static inline void *simap_force_add_internal(simap_instance *map, const char *key, void *ptr) {
|
||||||
uint32_t storage_needed = simap_elem_storage_size(key);
|
uint32_t storage_needed = simap_elem_storage_size(key);
|
||||||
assert((storage_needed % 8) == 0);
|
assert((storage_needed % 8) == 0);
|
||||||
if(SM_UNLIKELY(map->end - map->usage_end < storage_needed)) {
|
if(map->end - map->usage_end < storage_needed) {
|
||||||
/* Need storage */
|
/* Need storage */
|
||||||
aralloc(&(map->a),
|
aralloc(&(map->a),
|
||||||
sizeof(uint8_t)/*esize*/,
|
sizeof(uint8_t)/*esize*/,
|
||||||
@ -221,7 +198,7 @@ static inline void *simap_force_add(simap_instance *map, const char *key, void *
|
|||||||
/* XXX: The "padding" gets automagically added by the movement of the arena here(by junk bytes)! */
|
/* XXX: The "padding" gets automagically added by the movement of the arena here(by junk bytes)! */
|
||||||
|
|
||||||
/* Update previous with linkage */
|
/* Update previous with linkage */
|
||||||
if(SM_LIKELY(previ != (uint32_t)-1)) {
|
if(previ != (uint32_t)-1) {
|
||||||
uint32_t *prevnex = (uint32_t *)((uint8_t *)(map->base) + previ +
|
uint32_t *prevnex = (uint32_t *)((uint8_t *)(map->base) + previ +
|
||||||
sizeof(simap_ptr64) +
|
sizeof(simap_ptr64) +
|
||||||
sizeof(uint32_t));
|
sizeof(uint32_t));
|
||||||
@ -255,7 +232,7 @@ static inline void *simap_force_add(simap_instance *map, const char *key, void *
|
|||||||
static inline void* simap(void *amap_instance, AMAP_OP op, const char *key, void *ptr) {
|
static inline void* simap(void *amap_instance, AMAP_OP op, const char *key, void *ptr) {
|
||||||
simap_instance *map = (simap_instance *) amap_instance;
|
simap_instance *map = (simap_instance *) amap_instance;
|
||||||
|
|
||||||
if((op == AMAP_ERASE)) {
|
if(op == AMAP_ERASE) {
|
||||||
map->prev_usage_end = (uint32_t) -1;
|
map->prev_usage_end = (uint32_t) -1;
|
||||||
map->usage_end = 0;
|
map->usage_end = 0;
|
||||||
return (void *)((uint8_t)(NULL) - 1L);
|
return (void *)((uint8_t)(NULL) - 1L);
|
||||||
@ -269,13 +246,12 @@ static inline void* simap(void *amap_instance, AMAP_OP op, const char *key, void
|
|||||||
} else {
|
} else {
|
||||||
assert(op == AMAP_SET);
|
assert(op == AMAP_SET);
|
||||||
|
|
||||||
if((!found)) {
|
if(found) {
|
||||||
/* Add as new */
|
|
||||||
return simap_force_add(map, key, ptr);
|
|
||||||
} else {
|
|
||||||
/* Just overwrite */
|
/* Just overwrite */
|
||||||
found->ptr = ptr;
|
found->ptr = ptr;
|
||||||
return (void *) found;
|
return (void *) found;
|
||||||
|
} else {
|
||||||
|
return simap_force_add_internal(map, key, ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user