Last active
February 18, 2025 10:18
-
-
Save flomnes/0e0e70c6528dc8b601012da0ee7a11a2 to your computer and use it in GitHub Desktop.
benchmark hash_map vs string map
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <chrono> | |
#include <cstdlib> //rand | |
#include <iostream> | |
#include <map> | |
#include <string> | |
#include <unordered_map> | |
#include <boost/container_hash/hash.hpp> | |
constexpr int INV_REQ = 2; | |
namespace NB | |
{ | |
constexpr int VARIABLES = 100; | |
constexpr int SCENARIOS = 100; | |
constexpr int TIMESTEPS = 168; | |
} // namespace NB | |
struct Key; | |
struct ComponentVariable; | |
struct hash | |
{ | |
std::size_t operator()(const Key& k) const; | |
std::size_t operator()(const ComponentVariable& k) const; | |
}; | |
struct Key | |
{ | |
bool operator==(const Key& other) const | |
{ | |
return comp_id == other.comp_id && var_name == other.var_name && timestep == other.timestep | |
&& scenario == other.scenario; | |
} | |
// This may be a terrible comparison operator | |
bool operator<(const Key& other) const | |
{ | |
static hash h; | |
return h(*this) < h(other); | |
} | |
std::string comp_id; | |
std::string var_name; | |
int timestep; | |
int scenario; | |
}; | |
std::size_t hash::operator()(const Key& k) const | |
{ | |
std::size_t seed = 0; | |
boost::hash_combine(seed, k.comp_id); | |
boost::hash_combine(seed, k.var_name); | |
boost::hash_combine(seed, k.timestep); | |
boost::hash_combine(seed, k.scenario); | |
return seed; | |
} | |
using HashMap = std::unordered_map<Key, double, hash>; | |
std::vector<Key> fill(HashMap& m) | |
{ | |
srand(0); | |
std::vector<Key> keys; | |
for (int variable = 0; variable < NB::VARIABLES; variable++) | |
{ | |
for (int timeStep = 0; timeStep < NB::TIMESTEPS; timeStep++) | |
{ | |
for (int scenario = 0; scenario < NB::SCENARIOS; scenario++) | |
{ | |
const Key key{.comp_id = "component_" + std::to_string(rand() % 244), | |
.var_name = "variable_" + std::to_string(variable), | |
.timestep = timeStep, | |
.scenario = scenario}; | |
m[key] = rand(); | |
if ((rand() % INV_REQ) == 0) | |
{ | |
keys.push_back(key); | |
} | |
} | |
} | |
} | |
#ifdef DEBUG | |
std::cout << "container size " << m.size() << std::endl; | |
#endif | |
return keys; | |
} | |
double get(const HashMap& m, const std::vector<Key>& keys) | |
{ | |
#ifdef DEBUG | |
std::cout << "keys " << keys.size() << std::endl; | |
#endif | |
double x = 0; | |
for (const auto& key: keys) | |
{ | |
x += m.at(key); | |
} | |
return x; | |
} | |
using StringMap = std::map<std::string, double>; | |
std::vector<std::string> fill(StringMap& m) | |
{ | |
srand(0); | |
std::vector<std::string> keys; | |
for (int variable = 0; variable < NB::VARIABLES; variable++) | |
{ | |
for (int timeStep = 0; timeStep < NB::TIMESTEPS; timeStep++) | |
{ | |
for (int scenario = 0; scenario < NB::SCENARIOS; scenario++) | |
{ | |
const std::string key = "component_" + std::to_string(rand() % 244) + "variable_" | |
+ std::to_string(variable) + std::to_string(timeStep) + "-" | |
+ std::to_string(scenario); | |
m[key] = rand(); | |
if ((rand() % INV_REQ) == 0) | |
{ | |
keys.push_back(key); | |
} | |
} | |
} | |
} | |
#ifdef DEBUG | |
std::cout << "container size " << m.size() << std::endl; | |
#endif | |
return keys; | |
} | |
double get(const StringMap& m, const std::vector<std::string>& keys) | |
{ | |
#ifdef DEBUG | |
std::cout << "keys " << keys.size() << std::endl; | |
#endif | |
double x = 0; | |
for (const auto& key: keys) | |
{ | |
x += m.at(key); | |
} | |
return x; | |
} | |
struct ComponentVariable | |
{ | |
std::string component, variable; | |
bool operator==(const ComponentVariable& other) const | |
{ | |
return component == other.component && variable == other.variable; | |
} | |
}; | |
std::size_t hash::operator()(const ComponentVariable& k) const | |
{ | |
std::size_t seed = 0; | |
boost::hash_combine(seed, k.component); | |
boost::hash_combine(seed, k.variable); | |
return seed; | |
} | |
using HashMapVector = std::unordered_map<ComponentVariable, std::vector<std::vector<double>>, hash>; | |
struct ComponentVariableFullKey | |
{ | |
ComponentVariable cv; | |
int timeStep; | |
int scenario; | |
}; | |
std::vector<ComponentVariableFullKey> fill(HashMapVector& m) | |
{ | |
srand(0); | |
std::vector<ComponentVariableFullKey> keys; | |
for (int variable = 0; variable < NB::VARIABLES; variable++) | |
{ | |
std::string component = "component_" + std::to_string(rand() % 244); | |
ComponentVariable cv = {.component = component, | |
.variable = "variable_" + std::to_string(variable)}; | |
m[cv].resize(NB::TIMESTEPS); | |
for (auto& x: m[cv]) | |
{ | |
x.resize(NB::SCENARIOS); | |
} | |
for (int timeStep = 0; timeStep < NB::TIMESTEPS; timeStep++) | |
{ | |
for (int scenario = 0; scenario < NB::SCENARIOS; scenario++) | |
{ | |
ComponentVariableFullKey fullKey = {.cv = cv, | |
.timeStep = timeStep, | |
.scenario = scenario}; | |
m[cv][timeStep][scenario] = rand(); | |
if ((rand() % INV_REQ) == 0) | |
{ | |
keys.push_back(fullKey); | |
} | |
} | |
} | |
} | |
return keys; | |
} | |
double get(const HashMapVector& m, const std::vector<ComponentVariableFullKey>& keys) | |
{ | |
#ifdef DEBUG | |
std::cout << "keys " << keys.size() << std::endl; | |
#endif | |
double x = 0; | |
for (const auto& key: keys) | |
{ | |
x += m.at(key.cv)[key.timeStep][key.scenario]; | |
} | |
{ | |
std::size_t size = 0; | |
for (const auto& [k, v]: m) | |
{ | |
for (const auto& ts: v) | |
{ | |
for (const auto& sc: ts) | |
{ | |
size++; | |
} | |
} | |
} | |
#ifdef DEBUG | |
std::cout << "container size " << size << std::endl; | |
#endif | |
} | |
return x; | |
} | |
void measure_time(std::string msg, auto&& func) | |
{ | |
auto start = std::chrono::high_resolution_clock::now(); | |
func(); | |
auto end = std::chrono::high_resolution_clock::now(); | |
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); | |
std::cout << msg << " " << duration.count() << " ms\n"; | |
} | |
int main() | |
{ | |
{ | |
std::cout << "HashMap" << std::endl; | |
HashMap m; | |
std::vector<Key> keys; | |
measure_time("fill", [&m, &keys] { keys = fill(m); }); | |
measure_time("get", [&m, &keys] { volatile double k = get(m, keys); }); | |
} | |
{ | |
std::cout << "StringMap" << std::endl; | |
StringMap m; | |
std::vector<std::string> keys; | |
measure_time("fill", [&m, &keys] { keys = fill(m); }); | |
measure_time("get", [&m, &keys] { volatile double k = get(m, keys); }); | |
} | |
{ | |
std::cout << "HashMapVector" << std::endl; | |
HashMapVector m; | |
std::vector<ComponentVariableFullKey> keys; | |
measure_time("fill", [&m, &keys] { keys = fill(m); }); | |
measure_time("get", [&m, &keys] { volatile double k = get(m, keys); }); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment