From e82468acf829003226aa1adff9308287e8d9f691 Mon Sep 17 00:00:00 2001 From: Richard Thier Date: Wed, 23 Oct 2024 21:30:30 +0200 Subject: [PATCH] added simd_map_create_and_reserve --- main.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++++++++----- makefile | 2 + simd_map.h | 11 +++++ 3 files changed, 130 insertions(+), 12 deletions(-) diff --git a/main.cpp b/main.cpp index b4ad4de..3f274f3 100644 --- a/main.cpp +++ b/main.cpp @@ -33,7 +33,46 @@ inline const char *keystore(int i, bool create = false) noexcept { } /** - * Creates keys or returns the ith key. Used for performance tests. + * Creates keys or returns the ith integer key. Used for performance tests. + * + * Rem.: Generated keys are like this: i, i-1, i-2, ... 1 + * + * @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 int *int_keystore(int i, bool create = false) noexcept { + static thread_local std::vector keys; + + if(!create) { + return &(keys[i]); + } else { + keys.resize(0); + keys.reserve(0); + for(int j = 0; j < i; ++j) { + keys.push_back(i - j); + } + return NULL; + } +} +inline const char *datastore_int(int i, bool create = false) noexcept { + static thread_local std::vector 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 datas or returns the ith data. 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. @@ -124,7 +163,7 @@ void test_basics(amap mapdo, void *map) { assert(*iptr == 3); } -void test_stringmaps() { +void test_stringmaps(int perf_test_i) { /* Basic tests */ simap_instance si = simap_create(); test_basics(simap, &si); @@ -136,12 +175,7 @@ void test_stringmaps() { test_basics(unomap, &umi); /* Performance tests */ - int i = 100; - keystore(i, true); - datastore(i, true); - - puts("Performance testing stringmaps:"); - puts(""); + int i = perf_test_i; test_perf(mapmap, &mi, i, "std::map"); test_perf(simap, &si, i, "simap"); test_perf(unomap, &umi, i, "std::unordered_map"); @@ -214,14 +248,85 @@ void test_simd_map_basics() { simd_map_free(&smap); } -void test_intmaps() { +void test_simd_map_perf(int max_key) { +#ifdef __AVX2__ + puts("...Perf testing simd_map with AVX2..."); +#elif __SSE2__ + puts("...Perf testing simd_map with SSE2..."); +#endif + + // XXX: This way we would measure the wrong thing: + // simd_map smap = simd_map_create(); + // Why? To measure the right thing, not the first allocation! + simd_map smap = simd_map_create_and_reserve(); + auto begin = std::chrono::high_resolution_clock::now(); + for(int i = 0; i < max_key; ++i) { + int *key = int_keystore(i); + int *data = datastore(i); + simd_map_set(&smap, *key, *data); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + + printf("Insertion time for %d elements (simd_map): %.3f ms.\n", max_key, elapsed.count() * 1e-6); + simd_map_free(&smap); +} + +void test_int_unordered_map(int max_key) { + std::unordered_map smap; + auto begin = std::chrono::high_resolution_clock::now(); + for(int i = 0; i < max_key; ++i) { + int *key = int_keystore(i); + int *data = datastore(i); + smap[*key] = *data; + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + + printf("Insertion time for %d elements (std::unordered_map): %.3f ms.\n", max_key, elapsed.count() * 1e-6); +} + +void test_int_std_map(int max_key) { + std::map smap; + auto begin = std::chrono::high_resolution_clock::now(); + for(int i = 0; i < max_key; ++i) { + int *key = int_keystore(i); + int *data = datastore(i); + smap[*key] = *data; + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + + printf("Insertion time for %d elements (std::map): %.3f ms.\n", max_key, elapsed.count() * 1e-6); +} + +void test_intmaps(int perf_test_i) { /* Basic tests */ - test_simd_map_basics(); + // test_simd_map_basics(); + test_int_std_map(perf_test_i); + test_int_unordered_map(perf_test_i); + test_simd_map_perf(perf_test_i); } int main() { - test_intmaps(); - test_stringmaps(); + int perf_test_i = 100; + + /* Prepare data stores */ + keystore(perf_test_i, true); + int_keystore(perf_test_i, true); + datastore(perf_test_i, true); + + /* Tests */ + puts(""); + puts("Integer maps..."); + puts(""); + test_intmaps(perf_test_i); + puts(""); + puts("String maps..."); + puts(""); + test_stringmaps(perf_test_i); + puts(""); + puts("...done!"); return 0; } diff --git a/makefile b/makefile index e0aa3eb..f4ae209 100644 --- a/makefile +++ b/makefile @@ -6,5 +6,7 @@ debug-avx2: g++ main.cpp -g -mavx2 -Wall -o main release-avx2: g++ main.cpp -mavx2 -O2 -Wall -o main +release-avx2-debug: + g++ main.cpp -g -mavx2 -O2 -Wall -o main release-avx2-asm: g++ main.cpp -S -fopt-info-vec-missed -masm=intel -mavx2 -O2 -Wall -o main diff --git a/simd_map.h b/simd_map.h index 117f452..49e2f7a 100644 --- a/simd_map.h +++ b/simd_map.h @@ -63,6 +63,9 @@ static inline SM_ALWAYS_INLINE simd_map simd_map_create() { return ret; } +/** Creates a simd map instance and pre-reserve space for a few elements */ +static inline SM_ALWAYS_INLINE simd_map simd_map_create_and_reserve(); + /** Free all resources held by the map. Returns 0 on errors. */ static inline SM_ALWAYS_INLINE char simd_map_free(simd_map *map) { return freearena(&(map->a)); @@ -372,4 +375,12 @@ static inline int simd_map_remove(simd_map *map, uint32_t key) { return 0; } +/** Creates a simd map instance and pre-reserve space for a few elements */ +static inline SM_ALWAYS_INLINE simd_map simd_map_create_and_reserve() { + simd_map smap = simd_map_create(); + simd_map_set(&smap, 42, 42); + simd_map_erase(&smap); // Resets the map, but keeps memory reserved! + return smap; +} + #endif