Skip to content

Instantly share code, notes, and snippets.

@flomnes
Last active February 18, 2025 10:18
Show Gist options
  • Save flomnes/0e0e70c6528dc8b601012da0ee7a11a2 to your computer and use it in GitHub Desktop.
Save flomnes/0e0e70c6528dc8b601012da0ee7a11a2 to your computer and use it in GitHub Desktop.
benchmark hash_map vs string map
#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