2022-02-10 14:36:34 +01:00
|
|
|
//
|
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
|
|
|
|
//
|
|
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
//
|
2022-02-10 18:26:11 +01:00
|
|
|
|
|
|
|
#if USE_MEMPROF
|
|
|
|
#include "memprof/memprof_stat.h"
|
|
|
|
#endif
|
2022-02-10 16:30:03 +01:00
|
|
|
|
2022-02-10 14:36:34 +01:00
|
|
|
#include "td/utils/check.h"
|
|
|
|
#include "td/utils/FlatHashMap.h"
|
|
|
|
#include "td/utils/format.h"
|
2022-02-10 18:26:11 +01:00
|
|
|
#include "td/utils/misc.h"
|
|
|
|
#include "td/utils/port/Stat.h"
|
2022-02-10 16:30:03 +01:00
|
|
|
#include "td/utils/Slice.h"
|
2022-02-10 14:36:34 +01:00
|
|
|
|
|
|
|
#include <absl/container/flat_hash_map.h>
|
2022-02-10 16:30:03 +01:00
|
|
|
#include <folly/container/F14Map.h>
|
2022-02-10 14:36:34 +01:00
|
|
|
#include <map>
|
|
|
|
#include <unordered_map>
|
|
|
|
|
2022-02-10 18:26:11 +01:00
|
|
|
int mem_stat_i = -1;
|
|
|
|
int mem_stat_cur = 0;
|
|
|
|
bool use_memprof() {
|
|
|
|
return mem_stat_i < 0
|
|
|
|
#if USE_MEMPROF
|
|
|
|
&& is_memprof_on()
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
}
|
|
|
|
auto get_memory() {
|
|
|
|
#if USE_MEMPROF
|
|
|
|
if (use_memprof()) {
|
|
|
|
return get_used_memory_size();
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
CHECK(!use_memprof());
|
|
|
|
return td::mem_stat().ok().resident_size_;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2022-02-10 14:36:34 +01:00
|
|
|
template <class T>
|
|
|
|
class Generator {
|
|
|
|
public:
|
2022-02-10 16:30:03 +01:00
|
|
|
T next() {
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2022-02-10 16:30:23 +01:00
|
|
|
static size_t dyn_size() {
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2022-02-10 14:36:34 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
class IntGenerator {
|
|
|
|
public:
|
|
|
|
T next() {
|
|
|
|
return ++value;
|
|
|
|
}
|
2022-02-10 16:30:23 +01:00
|
|
|
static size_t dyn_size() {
|
|
|
|
return 0;
|
|
|
|
}
|
2022-02-10 16:30:03 +01:00
|
|
|
|
2022-02-10 14:36:34 +01:00
|
|
|
private:
|
|
|
|
T value{};
|
|
|
|
};
|
|
|
|
|
|
|
|
template <>
|
|
|
|
class Generator<uint32_t> : public IntGenerator<uint32_t> {
|
|
|
|
public:
|
|
|
|
};
|
|
|
|
template <>
|
|
|
|
class Generator<uint64_t> : public IntGenerator<uint64_t> {
|
|
|
|
public:
|
|
|
|
};
|
|
|
|
|
2022-02-10 16:30:23 +01:00
|
|
|
template <class T>
|
|
|
|
class Generator<td::unique_ptr<T>> {
|
|
|
|
public:
|
|
|
|
td::unique_ptr<T> next() {
|
|
|
|
return td::make_unique<T>();
|
|
|
|
}
|
|
|
|
static size_t dyn_size() {
|
|
|
|
return sizeof(T);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-02-10 14:36:34 +01:00
|
|
|
template <class T, class KeyT, class ValueT>
|
|
|
|
void measure(td::StringBuilder &sb, td::Slice name, td::Slice key_name, td::Slice value_name) {
|
2022-02-10 18:26:11 +01:00
|
|
|
mem_stat_cur++;
|
|
|
|
if (mem_stat_i >= 0 && mem_stat_cur != mem_stat_i) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sb << name << "<" << key_name << "," << value_name << "> " << (use_memprof() ? "memprof" : "os") << "\n";
|
2022-02-10 16:30:23 +01:00
|
|
|
size_t ideal_size = sizeof(KeyT) + sizeof(ValueT) + Generator<ValueT>::dyn_size();
|
2022-02-10 14:36:34 +01:00
|
|
|
|
|
|
|
sb << "\tempty:" << sizeof(T);
|
|
|
|
struct Stat {
|
|
|
|
int pi;
|
|
|
|
double min_ratio;
|
|
|
|
double max_ratio;
|
|
|
|
};
|
|
|
|
std::vector<Stat> stat;
|
|
|
|
stat.reserve(1024);
|
|
|
|
for (size_t size : {10000000u}) {
|
|
|
|
Generator<KeyT> key_generator;
|
2022-02-10 16:30:23 +01:00
|
|
|
Generator<ValueT> value_generator;
|
2022-02-10 18:26:11 +01:00
|
|
|
auto start_mem = get_memory();
|
2022-02-10 14:36:34 +01:00
|
|
|
T ht;
|
|
|
|
auto ratio = [&]() {
|
2022-02-10 18:26:11 +01:00
|
|
|
auto end_mem = get_memory();
|
2022-02-10 14:36:34 +01:00
|
|
|
auto used_mem = end_mem - start_mem;
|
|
|
|
return double(used_mem) / double(ideal_size * ht.size());
|
|
|
|
};
|
|
|
|
double min_ratio;
|
|
|
|
double max_ratio;
|
|
|
|
auto reset = [&]() {
|
|
|
|
min_ratio = 1e100;
|
|
|
|
max_ratio = 0;
|
|
|
|
};
|
|
|
|
auto update = [&]() {
|
|
|
|
auto x = ratio();
|
|
|
|
min_ratio = std::min(min_ratio, x);
|
|
|
|
max_ratio = std::max(max_ratio, x);
|
|
|
|
};
|
|
|
|
reset();
|
|
|
|
|
|
|
|
int p = 10;
|
|
|
|
int pi = 1;
|
|
|
|
for (size_t i = 0; i < size; i++) {
|
2022-02-10 16:30:23 +01:00
|
|
|
ht.emplace(key_generator.next(), value_generator.next());
|
2022-02-10 14:36:34 +01:00
|
|
|
update();
|
|
|
|
if ((i + 1) % p == 0) {
|
|
|
|
stat.emplace_back(Stat{pi, min_ratio, max_ratio});
|
|
|
|
reset();
|
|
|
|
pi++;
|
|
|
|
p *= 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto &s : stat) {
|
|
|
|
sb << " " << 10 << "^" << s.pi << ":" << s.min_ratio << "->" << s.max_ratio;
|
|
|
|
}
|
|
|
|
sb << "\n";
|
|
|
|
}
|
|
|
|
|
2022-02-10 16:30:23 +01:00
|
|
|
template <size_t size>
|
|
|
|
using Bytes = std::array<uint8_t, size>;
|
|
|
|
|
2022-02-10 18:26:11 +01:00
|
|
|
template <template <typename... Args> class T>
|
2022-02-10 14:36:34 +01:00
|
|
|
void print_memory_stats(td::Slice name) {
|
2022-02-10 16:30:03 +01:00
|
|
|
std::string big_buff(1 << 16, '\0');
|
2022-02-10 14:36:34 +01:00
|
|
|
td::StringBuilder sb(big_buff, false);
|
2022-02-10 18:26:11 +01:00
|
|
|
#define MEASURE(KeyT, ValueT) measure<T<KeyT, ValueT>, KeyT, ValueT>(sb, name, #KeyT, #ValueT);
|
2022-02-10 16:30:23 +01:00
|
|
|
MEASURE(uint32_t, uint32_t);
|
|
|
|
MEASURE(uint64_t, td::unique_ptr<Bytes<360>>);
|
2022-02-10 18:26:11 +01:00
|
|
|
if (!sb.as_cslice().empty()) {
|
|
|
|
LOG(PLAIN) << "\n" << sb.as_cslice() << "\n";
|
|
|
|
}
|
2022-02-10 14:36:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#define FOR_EACH_TABLE(F) \
|
|
|
|
F(td::FlatHashMapImpl) \
|
|
|
|
F(folly::F14FastMap) \
|
|
|
|
F(absl::flat_hash_map) \
|
|
|
|
F(std::unordered_map) \
|
|
|
|
F(std::map)
|
|
|
|
#define BENCH_MEMORY(T) print_memory_stats<T>(#T);
|
|
|
|
|
2022-02-10 18:26:11 +01:00
|
|
|
int main(int argc, const char *argv[]) {
|
|
|
|
// Usage:
|
|
|
|
// % benchmark/memory-hashset-os 0
|
|
|
|
// max_i = 10
|
|
|
|
// % for i in {1..10}; do ./benchmark/memory-hashset-os $i; done
|
|
|
|
if (argc > 1) {
|
|
|
|
mem_stat_i = td::to_integer<td::int32>(td::Slice(argv[1]));
|
|
|
|
}
|
2022-02-10 14:36:34 +01:00
|
|
|
FOR_EACH_TABLE(BENCH_MEMORY);
|
2022-02-10 18:26:11 +01:00
|
|
|
if (mem_stat_i <= 0) {
|
|
|
|
LOG(PLAIN) << "max_i = " << mem_stat_cur << "\n";
|
|
|
|
}
|
2022-02-10 14:36:34 +01:00
|
|
|
return 0;
|
|
|
|
}
|