Created
May 15, 2025 13:59
-
-
Save eao197/32042a91c84d1ee10729828d55de0ec1 to your computer and use it in GitHub Desktop.
Сравнение скорости std::unordered_map::find с разными типами ключей
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 <array> | |
#include <chrono> | |
#include <iostream> | |
#include <string> | |
#include <string_view> | |
#include <unordered_map> | |
#include <utility> | |
namespace test | |
{ | |
using full_key_t = std::pair<std::size_t, std::string>; | |
using light_key_t = std::pair<std::size_t, const std::string_view*>; | |
[[nodiscard]] | |
const std::string & | |
second_key_part(const full_key_t & k) noexcept | |
{ | |
return k.second; | |
} | |
[[nodiscard]] | |
const std::string_view & | |
second_key_part(const light_key_t & k) noexcept | |
{ | |
return *(k.second); | |
} | |
struct special_hash_t | |
{ | |
using is_transparent = void; | |
std::size_t | |
operator()(const full_key_t & k) const noexcept | |
{ | |
return std::hash<std::size_t>{}(k.first) + 0x9e3779b9 + | |
std::hash<std::string>{}(second_key_part(k)); | |
} | |
std::size_t | |
operator()(const light_key_t & k) const noexcept | |
{ | |
return std::hash<std::size_t>{}(k.first) + 0x9e3779b9 + | |
std::hash<std::string_view>{}(second_key_part(k)); | |
} | |
}; | |
struct special_equal_to_t | |
{ | |
using is_transparent = void; | |
template<typename K1, typename K2> | |
bool | |
operator()(const K1 & k1, const K2 & k2) const noexcept | |
{ | |
return (k1.first == k2.first) && | |
(second_key_part(k1) == second_key_part(k2)); | |
} | |
}; | |
using values_map_t = std::unordered_map< | |
full_key_t, | |
int, | |
special_hash_t, | |
special_equal_to_t>; | |
#if 0 | |
const std::array<std::string, 10> strings{ | |
"a rather long string value to avoid small string optimization 001", | |
"a rather long string value to avoid small string optimization 002", | |
"a rather long string value to avoid small string optimization 003", | |
"a rather long string value to avoid small string optimization 004", | |
"a rather long string value to avoid small string optimization 005", | |
"a rather long string value to avoid small string optimization 006", | |
"a rather long string value to avoid small string optimization 007", | |
"a rather long string value to avoid small string optimization 008", | |
"a rather long string value to avoid small string optimization 009", | |
"a rather long string value to avoid small string optimization 010" | |
}; | |
#else | |
const std::array<std::string, 10> strings{ | |
"str 001", | |
"str 002", | |
"str 003", | |
"str 004", | |
"str 005", | |
"str 006", | |
"str 007", | |
"str 008", | |
"str 009", | |
"str 010" | |
}; | |
#endif | |
const std::size_t map_size = 1000; | |
const std::size_t insertions = 10'000'000; | |
[[nodiscard]] | |
values_map_t | |
make_values() | |
{ | |
values_map_t result; | |
int value = 0; | |
for(std::size_t i = 0; i != map_size; ++i, ++value) | |
{ | |
const std::string & sk = strings[i % std::size(strings)]; | |
result.emplace(full_key_t{ i, sk }, value); | |
} | |
return result; | |
} | |
[[nodiscard]] | |
std::pair<std::size_t, const std::string &> | |
keys_from_i(std::size_t i) | |
{ | |
return { i % map_size, strings[i % std::size(strings)] }; | |
} | |
std::size_t | |
find_by_full_key( | |
values_map_t & values) | |
{ | |
std::size_t result{}; | |
for(std::size_t i = 0; i != insertions; ++i) | |
{ | |
const auto keys = keys_from_i(i); | |
if(const auto it = values.find( | |
full_key_t{ keys.first, keys.second }); | |
it != values.end()) | |
{ | |
++result; | |
} | |
} | |
return result; | |
} | |
std::size_t | |
find_by_light_key( | |
values_map_t & values) | |
{ | |
std::size_t result{}; | |
for(std::size_t i = 0; i != insertions; ++i) | |
{ | |
const auto keys = keys_from_i(i); | |
const std::string_view str_part{ keys.second }; | |
if(const auto it = values.find( | |
light_key_t{ keys.first, std::addressof(str_part) }); | |
it != values.end()) | |
{ | |
++result; | |
} | |
} | |
return result; | |
} | |
} /* namespace test */ | |
class duration_meter | |
{ | |
const char * _name; | |
const std::chrono::high_resolution_clock::time_point _started_at; | |
public: | |
duration_meter( const char * name ) | |
: _name{ name } | |
, _started_at{ std::chrono::high_resolution_clock::now() } | |
{} | |
~duration_meter() | |
{ | |
const auto f = std::chrono::high_resolution_clock::now(); | |
std::cout << "*** " << _name << ": " | |
<< std::chrono::duration_cast<std::chrono::microseconds>( | |
f - _started_at ).count() | |
<< "us *** " << std::endl; | |
} | |
}; | |
template<typename Lambda> | |
decltype(auto) | |
measure( const char * name, Lambda && lambda ) | |
{ | |
duration_meter meter{ name }; | |
return lambda(); | |
} | |
using namespace test; | |
int main() | |
{ | |
auto values = make_values(); | |
const auto r1 = measure( "find_by_full_key", [&values]() { | |
return find_by_full_key(values); | |
}); | |
std::cout << "values found: " << r1 << std::endl;; | |
const auto r2 = measure( "find_by_light_key", [&values]() { | |
return find_by_light_key(values); | |
}); | |
std::cout << "values found: " << r2 << std::endl;; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment