initial
This commit is contained in:
93
td/benchmark/CMakeLists.txt
Normal file
93
td/benchmark/CMakeLists.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
if ((CMAKE_MAJOR_VERSION LESS 3) OR (CMAKE_VERSION VERSION_LESS "3.0.2"))
|
||||
message(FATAL_ERROR "CMake >= 3.0.2 is required")
|
||||
endif()
|
||||
|
||||
if (NOT OPENSSL_FOUND)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
endif()
|
||||
|
||||
# TODO: all benchmarks in one file
|
||||
add_executable(bench_crypto bench_crypto.cpp)
|
||||
target_link_libraries(bench_crypto PRIVATE tdutils ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES})
|
||||
if (WIN32)
|
||||
if (MINGW)
|
||||
target_link_libraries(bench_crypto PRIVATE ws2_32 mswsock crypt32)
|
||||
else()
|
||||
target_link_libraries(bench_crypto PRIVATE ws2_32 Mswsock Crypt32)
|
||||
endif()
|
||||
endif()
|
||||
target_include_directories(bench_crypto SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})
|
||||
|
||||
add_executable(bench_actor bench_actor.cpp)
|
||||
target_link_libraries(bench_actor PRIVATE tdactor tdutils)
|
||||
|
||||
add_executable(bench_http bench_http.cpp)
|
||||
target_link_libraries(bench_http PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_http_server bench_http_server.cpp)
|
||||
target_link_libraries(bench_http_server PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_http_server_cheat bench_http_server_cheat.cpp)
|
||||
target_link_libraries(bench_http_server_cheat PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_http_server_fast bench_http_server_fast.cpp)
|
||||
target_link_libraries(bench_http_server_fast PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_http_reader bench_http_reader.cpp)
|
||||
target_link_libraries(bench_http_reader PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_handshake bench_handshake.cpp)
|
||||
target_link_libraries(bench_handshake PRIVATE tdmtproto tdutils)
|
||||
|
||||
add_executable(bench_db bench_db.cpp)
|
||||
target_link_libraries(bench_db PRIVATE tdactor tddb tdutils)
|
||||
|
||||
add_executable(bench_tddb bench_tddb.cpp)
|
||||
target_link_libraries(bench_tddb PRIVATE tdcore tddb tdutils)
|
||||
|
||||
add_executable(bench_misc bench_misc.cpp)
|
||||
target_link_libraries(bench_misc PRIVATE tdcore tdutils)
|
||||
|
||||
add_executable(check_proxy check_proxy.cpp)
|
||||
target_link_libraries(check_proxy PRIVATE tdclient tdutils)
|
||||
|
||||
add_executable(check_tls check_tls.cpp)
|
||||
target_link_libraries(check_tls PRIVATE tdutils)
|
||||
|
||||
add_executable(rmdir rmdir.cpp)
|
||||
target_link_libraries(rmdir PRIVATE tdutils)
|
||||
|
||||
add_executable(wget wget.cpp)
|
||||
target_link_libraries(wget PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_empty bench_empty.cpp)
|
||||
target_link_libraries(bench_empty PRIVATE tdutils)
|
||||
|
||||
if (NOT WIN32 AND NOT CYGWIN)
|
||||
add_executable(bench_log bench_log.cpp)
|
||||
target_link_libraries(bench_log PRIVATE tdutils)
|
||||
|
||||
set_source_files_properties(bench_queue.cpp PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations)
|
||||
add_executable(bench_queue bench_queue.cpp)
|
||||
target_link_libraries(bench_queue PRIVATE tdutils)
|
||||
endif()
|
||||
|
||||
if (TD_TEST_FOLLY AND TD_WITH_ABSEIL)
|
||||
find_package(ABSL QUIET)
|
||||
find_package(folly QUIET)
|
||||
find_package(gflags QUIET)
|
||||
|
||||
if (ABSL_FOUND AND folly_FOUND)
|
||||
add_executable(memory-hashset-memprof EXCLUDE_FROM_ALL hashset_memory.cpp)
|
||||
target_compile_definitions(memory-hashset-memprof PRIVATE USE_MEMPROF=1)
|
||||
target_link_libraries(memory-hashset-memprof PRIVATE tdutils memprof_stat Folly::folly absl::flat_hash_map absl::hash)
|
||||
|
||||
add_executable(memory-hashset-os hashset_memory.cpp)
|
||||
target_compile_definitions(memory-hashset-os PRIVATE USE_MEMPROF=0)
|
||||
target_link_libraries(memory-hashset-os PRIVATE tdutils Folly::folly absl::flat_hash_map absl::hash)
|
||||
|
||||
add_executable(hashmap-build hashmap_build.cpp)
|
||||
target_link_libraries(hashmap-build PRIVATE tdutils Folly::folly absl::flat_hash_map absl::hash)
|
||||
endif()
|
||||
endif()
|
||||
349
td/benchmark/bench_actor.cpp
Normal file
349
td/benchmark/bench_actor.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
#include "td/actor/PromiseFuture.h"
|
||||
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Promise.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
|
||||
#if TD_MSVC
|
||||
#pragma comment(linker, "/STACK:16777216")
|
||||
#endif
|
||||
|
||||
struct TestActor final : public td::Actor {
|
||||
static td::int32 actor_count_;
|
||||
|
||||
void start_up() final {
|
||||
actor_count_++;
|
||||
stop();
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
if (--actor_count_ == 0) {
|
||||
td::Scheduler::instance()->finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
td::int32 TestActor::actor_count_;
|
||||
|
||||
namespace td {
|
||||
template <>
|
||||
class ActorTraits<TestActor> {
|
||||
public:
|
||||
static constexpr bool need_context = false;
|
||||
static constexpr bool need_start_up = true;
|
||||
};
|
||||
} // namespace td
|
||||
|
||||
class CreateActorBench final : public td::Benchmark {
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
|
||||
void start_up() final {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(0, 0);
|
||||
scheduler_->start();
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
scheduler_->finish();
|
||||
scheduler_.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
return "CreateActor";
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
scheduler_->create_actor_unsafe<TestActor>(0, "TestActor").release();
|
||||
}
|
||||
while (scheduler_->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <int type>
|
||||
class RingBench final : public td::Benchmark {
|
||||
public:
|
||||
struct PassActor;
|
||||
|
||||
private:
|
||||
int actor_n_ = -1;
|
||||
int thread_n_ = -1;
|
||||
td::vector<td::ActorId<PassActor>> actor_array_;
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
static const char *types[] = {"later", "immediate", "raw", "tail", "lambda"};
|
||||
static_assert(0 <= type && type < 5, "");
|
||||
return PSTRING() << "Ring (send_" << types[type] << ") (threads_n = " << thread_n_ << ")";
|
||||
}
|
||||
|
||||
struct PassActor final : public td::Actor {
|
||||
int id = -1;
|
||||
td::ActorId<PassActor> next_actor;
|
||||
int start_n = 0;
|
||||
|
||||
void pass(int n) {
|
||||
// LOG(INFO) << "Pass: " << n;
|
||||
if (n == 0) {
|
||||
td::Scheduler::instance()->finish();
|
||||
} else {
|
||||
if (type == 0) {
|
||||
send_closure_later(next_actor, &PassActor::pass, n - 1);
|
||||
} else if (type == 1) {
|
||||
send_closure(next_actor, &PassActor::pass, n - 1);
|
||||
} else if (type == 2) {
|
||||
send_event(next_actor, td::Event::raw(static_cast<td::uint32>(n - 1)));
|
||||
} else if (type == 3) {
|
||||
if (n % 5000 == 0) {
|
||||
send_closure_later(next_actor, &PassActor::pass, n - 1);
|
||||
} else {
|
||||
// TODO: it is three times faster than send_event
|
||||
// maybe send event could be further optimized?
|
||||
next_actor.get_actor_unsafe()->raw_event(td::Event::raw(static_cast<td::uint32>(n - 1)).data);
|
||||
}
|
||||
} else if (type == 4) {
|
||||
send_lambda(next_actor, [n, ptr = next_actor.get_actor_unsafe()] { ptr->pass(n - 1); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void raw_event(const td::Event::Raw &raw) final {
|
||||
pass(static_cast<int>(raw.u32));
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
yield();
|
||||
}
|
||||
void wakeup() final {
|
||||
if (start_n != 0) {
|
||||
int n = start_n;
|
||||
start_n = 0;
|
||||
pass(n);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RingBench(int actor_n, int thread_n) : actor_n_(actor_n), thread_n_(thread_n) {
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(thread_n_, 0);
|
||||
|
||||
actor_array_ = td::vector<td::ActorId<PassActor>>(actor_n_);
|
||||
for (int i = 0; i < actor_n_; i++) {
|
||||
actor_array_[i] =
|
||||
scheduler_->create_actor_unsafe<PassActor>(thread_n_ ? i % thread_n_ : 0, "PassActor").release();
|
||||
actor_array_[i].get_actor_unsafe()->id = i;
|
||||
}
|
||||
for (int i = 0; i < actor_n_; i++) {
|
||||
actor_array_[i].get_actor_unsafe()->next_actor = actor_array_[(i + 1) % actor_n_];
|
||||
}
|
||||
scheduler_->start();
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
// first actor is on main_thread
|
||||
actor_array_[0].get_actor_unsafe()->start_n = td::max(n, 100);
|
||||
while (scheduler_->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
scheduler_->finish();
|
||||
scheduler_.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <int type>
|
||||
class QueryBench final : public td::Benchmark {
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
static const char *types[] = {"callback", "immediate future", "delayed future", "dummy", "lambda", "lambda_future"};
|
||||
static_assert(0 <= type && type < 6, "");
|
||||
return PSTRING() << "QueryBench: " << types[type];
|
||||
}
|
||||
|
||||
class ClientActor final : public td::Actor {
|
||||
public:
|
||||
class Callback {
|
||||
public:
|
||||
Callback() = default;
|
||||
Callback(const Callback &) = delete;
|
||||
Callback &operator=(const Callback &) = delete;
|
||||
Callback(Callback &&) = delete;
|
||||
Callback &operator=(Callback &&) = delete;
|
||||
virtual ~Callback() = default;
|
||||
virtual void on_result(int x) = 0;
|
||||
};
|
||||
explicit ClientActor(td::unique_ptr<Callback> callback) : callback_(std::move(callback)) {
|
||||
}
|
||||
void f(int x) {
|
||||
callback_->on_result(x * x);
|
||||
}
|
||||
void dummy(int x, int *y) {
|
||||
*y = x * x;
|
||||
}
|
||||
void f_immediate_promise(int x, td::PromiseActor<int> &&promise) {
|
||||
promise.set_value(x * x);
|
||||
}
|
||||
void f_promise(td::Promise<> promise) {
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
|
||||
private:
|
||||
td::unique_ptr<Callback> callback_;
|
||||
};
|
||||
|
||||
class ServerActor final : public td::Actor {
|
||||
public:
|
||||
class ClientCallback final : public ClientActor::Callback {
|
||||
public:
|
||||
explicit ClientCallback(td::ActorId<ServerActor> server) : server_(server) {
|
||||
}
|
||||
void on_result(int x) final {
|
||||
send_closure(server_, &ServerActor::on_result, x);
|
||||
}
|
||||
|
||||
private:
|
||||
td::ActorId<ServerActor> server_;
|
||||
};
|
||||
void start_up() final {
|
||||
client_ = td::create_actor<ClientActor>("Client", td::make_unique<ClientCallback>(actor_id(this))).release();
|
||||
}
|
||||
|
||||
void on_result(int x) {
|
||||
CHECK(x == n_ * n_);
|
||||
wakeup();
|
||||
}
|
||||
|
||||
void wakeup() final {
|
||||
while (true) {
|
||||
if (n_ < 0) {
|
||||
td::Scheduler::instance()->finish();
|
||||
return;
|
||||
}
|
||||
n_--;
|
||||
if (type == 0) {
|
||||
send_closure(client_, &ClientActor::f, n_);
|
||||
return;
|
||||
} else if (type == 1) {
|
||||
td::PromiseActor<int> promise;
|
||||
td::FutureActor<int> future;
|
||||
init_promise_future(&promise, &future);
|
||||
send_closure(client_, &ClientActor::f_immediate_promise, n_, std::move(promise));
|
||||
CHECK(!future.is_ready());
|
||||
CHECK(!future.empty());
|
||||
CHECK(future.get_state() == td::FutureActor<int>::State::Waiting);
|
||||
// int val = future.move_as_ok();
|
||||
// CHECK(val == n_ * n_);
|
||||
} else if (type == 2) {
|
||||
td::PromiseActor<int> promise;
|
||||
init_promise_future(&promise, &future_);
|
||||
future_.set_event(td::EventCreator::raw(actor_id(), static_cast<td::uint64>(1)));
|
||||
send_closure(client_, &ClientActor::f_immediate_promise, n_, std::move(promise));
|
||||
return;
|
||||
} else if (type == 3) {
|
||||
int res;
|
||||
send_closure(client_, &ClientActor::dummy, n_, &res);
|
||||
} else if (type == 4) {
|
||||
int val = 0;
|
||||
send_lambda(client_, [&] { val = n_ * n_; });
|
||||
CHECK(val == 0 || val == n_ * n_);
|
||||
} else if (type == 5) {
|
||||
send_closure(client_, &ClientActor::f_promise,
|
||||
td::PromiseCreator::lambda([actor_id = actor_id(this), n = n_](td::Unit) {
|
||||
send_closure(actor_id, &ServerActor::result, n * n);
|
||||
}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run(int n) {
|
||||
n_ = n;
|
||||
wakeup();
|
||||
}
|
||||
|
||||
void raw_event(const td::Event::Raw &event) final {
|
||||
int val = future_.move_as_ok();
|
||||
CHECK(val == n_ * n_);
|
||||
wakeup();
|
||||
}
|
||||
void result(int val) {
|
||||
CHECK(val == n_ * n_);
|
||||
wakeup();
|
||||
}
|
||||
|
||||
private:
|
||||
td::ActorId<ClientActor> client_;
|
||||
int n_ = 0;
|
||||
td::FutureActor<int> future_;
|
||||
};
|
||||
|
||||
void start_up() final {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(0, 0);
|
||||
|
||||
server_ = scheduler_->create_actor_unsafe<ServerActor>(0, "Server");
|
||||
scheduler_->start();
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
// first actor is on main_thread
|
||||
{
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
send_closure(server_, &ServerActor::run, n);
|
||||
}
|
||||
while (scheduler_->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
server_.release();
|
||||
scheduler_->finish();
|
||||
scheduler_.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
td::ActorOwn<ServerActor> server_;
|
||||
};
|
||||
|
||||
int main() {
|
||||
td::init_openssl_threads();
|
||||
|
||||
bench(CreateActorBench());
|
||||
bench(RingBench<4>(504, 0));
|
||||
bench(RingBench<3>(504, 0));
|
||||
bench(RingBench<0>(504, 0));
|
||||
bench(RingBench<1>(504, 0));
|
||||
bench(RingBench<2>(504, 0));
|
||||
bench(QueryBench<5>());
|
||||
bench(QueryBench<4>());
|
||||
bench(QueryBench<3>());
|
||||
bench(QueryBench<2>());
|
||||
bench(QueryBench<1>());
|
||||
bench(QueryBench<0>());
|
||||
bench(RingBench<3>(504, 0));
|
||||
bench(RingBench<0>(504, 10));
|
||||
bench(RingBench<1>(504, 10));
|
||||
bench(RingBench<2>(504, 10));
|
||||
bench(RingBench<0>(504, 2));
|
||||
bench(RingBench<1>(504, 2));
|
||||
bench(RingBench<2>(504, 2));
|
||||
}
|
||||
525
td/benchmark/bench_crypto.cpp
Normal file
525
td/benchmark/bench_crypto.cpp
Normal file
@@ -0,0 +1,525 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/utils/benchmark.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/UInt.h"
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
static constexpr int DATA_SIZE = 8 << 10;
|
||||
static constexpr int SHORT_DATA_SIZE = 64;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER <= 0x10100000L
|
||||
class SHA1Bench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "SHA1 OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
unsigned char md[20];
|
||||
SHA1(data, DATA_SIZE, md);
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class SHA1ShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "SHA1 [" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
unsigned char md[20];
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::sha1(td::Slice(data, SHORT_DATA_SIZE), md);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SHA256ShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "SHA256 [" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
unsigned char md[32];
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::sha256(td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 32));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SHA512ShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "SHA512 [" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
unsigned char md[64];
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::sha512(td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 64));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HmacSha256ShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "HMAC-SHA256 [" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
unsigned char md[32];
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::hmac_sha256(td::Slice(data, SHORT_DATA_SIZE), td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 32));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HmacSha512ShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "HMAC-SHA512 [" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
unsigned char md[32];
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::hmac_sha256(td::Slice(data, SHORT_DATA_SIZE), td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 32));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AesEcbBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt256 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES ECB OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(key.raw, sizeof(key));
|
||||
td::Random::secure_bytes(iv.raw, sizeof(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::AesState state;
|
||||
state.init(td::as_slice(key), true);
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
for (int i = 0; i <= n; i++) {
|
||||
size_t step = 16;
|
||||
for (size_t offset = 0; offset + step <= data_slice.size(); offset += step) {
|
||||
state.encrypt(data_slice.ubegin() + offset, data_slice.ubegin() + offset, static_cast<int>(step));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AesIgeEncryptBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt256 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES IGE OpenSSL encrypt [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(key.raw, sizeof(key));
|
||||
td::Random::secure_bytes(iv.raw, sizeof(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
td::AesIgeState state;
|
||||
state.init(as_slice(key), as_slice(iv), true);
|
||||
for (int i = 0; i < n; i++) {
|
||||
state.encrypt(data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AesIgeDecryptBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt256 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES IGE OpenSSL decrypt [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(key.raw, sizeof(key));
|
||||
td::Random::secure_bytes(iv.raw, sizeof(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
td::AesIgeState state;
|
||||
state.init(as_slice(key), as_slice(iv), false);
|
||||
for (int i = 0; i < n; i++) {
|
||||
state.decrypt(data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AesCtrBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt128 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES CTR OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(key.raw, sizeof(key));
|
||||
td::Random::secure_bytes(iv.raw, sizeof(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
td::AesCtrState state;
|
||||
state.init(as_slice(key), as_slice(iv));
|
||||
for (int i = 0; i < n; i++) {
|
||||
state.encrypt(data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
class AesCtrOpenSSLBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt128 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES CTR RAW OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(key.raw, sizeof(key));
|
||||
td::Random::secure_bytes(iv.raw, sizeof(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||
EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), nullptr, key.raw, iv.raw);
|
||||
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
td::AesCtrState state;
|
||||
state.init(as_slice(key), as_slice(iv));
|
||||
for (int i = 0; i < n; i++) {
|
||||
int len = 0;
|
||||
EVP_EncryptUpdate(ctx, data_slice.ubegin(), &len, data_slice.ubegin(), DATA_SIZE);
|
||||
CHECK(len == DATA_SIZE);
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class AesCbcDecryptBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt128 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES CBC Decrypt OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(as_mutable_slice(key));
|
||||
td::Random::secure_bytes(as_mutable_slice(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::aes_cbc_decrypt(as_slice(key), as_mutable_slice(iv), data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AesCbcEncryptBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt128 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES CBC Encrypt OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(as_mutable_slice(key));
|
||||
td::Random::secure_bytes(as_mutable_slice(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::aes_cbc_encrypt(as_slice(key), as_mutable_slice(iv), data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <bool use_state>
|
||||
class AesIgeShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt256 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES IGE OpenSSL " << (use_state ? "EVP" : "C ") << "[" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(as_mutable_slice(key));
|
||||
td::Random::secure_bytes(as_mutable_slice(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, SHORT_DATA_SIZE);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (use_state) {
|
||||
td::AesIgeState ige;
|
||||
ige.init(as_slice(key), as_slice(iv), false);
|
||||
ige.decrypt(data_slice, data_slice);
|
||||
} else {
|
||||
td::aes_ige_decrypt(as_slice(key), as_mutable_slice(iv), data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BENCH(Rand, "std_rand") {
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res ^= std::rand();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(CppRand, "mt19937_rand") {
|
||||
std::uint_fast32_t res = 0;
|
||||
std::mt19937 g(123);
|
||||
for (int i = 0; i < n; i++) {
|
||||
res ^= g();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(TdRand32, "td_rand_fast32") {
|
||||
td::uint32 res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res ^= td::Random::fast_uint32();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(TdRandFast, "td_rand_fast") {
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res ^= td::Random::fast(0, RAND_MAX);
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
BENCH(SslRand, "ssl_rand_int32") {
|
||||
std::vector<td::thread> v;
|
||||
std::atomic<td::uint32> sum{0};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
v.emplace_back([&sum, n] {
|
||||
td::int32 res = 0;
|
||||
for (int j = 0; j < n; j++) {
|
||||
res ^= td::Random::secure_int32();
|
||||
}
|
||||
sum += res;
|
||||
});
|
||||
}
|
||||
for (auto &x : v) {
|
||||
x.join();
|
||||
}
|
||||
v.clear();
|
||||
td::do_not_optimize_away(sum.load());
|
||||
}
|
||||
#endif
|
||||
|
||||
BENCH(SslRandBuf, "ssl_rand_bytes") {
|
||||
td::int32 res = 0;
|
||||
std::array<td::int32, 1000> buf;
|
||||
for (int i = 0; i < n; i += static_cast<int>(buf.size())) {
|
||||
td::Random::secure_bytes(reinterpret_cast<td::uint8 *>(buf.data()), sizeof(buf[0]) * buf.size());
|
||||
for (auto x : buf) {
|
||||
res ^= x;
|
||||
}
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(Pbkdf2, "pbkdf2") {
|
||||
std::string password = "cucumber";
|
||||
std::string salt = "abcdefghijklmnopqrstuvw";
|
||||
std::string key(32, ' ');
|
||||
td::pbkdf2_sha256(password, salt, n, key);
|
||||
}
|
||||
|
||||
class Crc32Bench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "CRC32 zlib [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::uint64 res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += td::crc32(td::Slice(data, DATA_SIZE));
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
};
|
||||
|
||||
class Crc64Bench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "CRC64 Anton [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::uint64 res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += td::crc64(td::Slice(data, DATA_SIZE));
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
td::init_openssl_threads();
|
||||
td::bench(AesCtrBench());
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
td::bench(AesCtrOpenSSLBench());
|
||||
#endif
|
||||
|
||||
td::bench(AesCbcDecryptBench());
|
||||
td::bench(AesCbcEncryptBench());
|
||||
td::bench(AesIgeShortBench<true>());
|
||||
td::bench(AesIgeShortBench<false>());
|
||||
td::bench(AesIgeEncryptBench());
|
||||
td::bench(AesIgeDecryptBench());
|
||||
td::bench(AesEcbBench());
|
||||
|
||||
td::bench(Pbkdf2Bench());
|
||||
td::bench(RandBench());
|
||||
td::bench(CppRandBench());
|
||||
td::bench(TdRand32Bench());
|
||||
td::bench(TdRandFastBench());
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
td::bench(SslRandBench());
|
||||
#endif
|
||||
td::bench(SslRandBufBench());
|
||||
#if OPENSSL_VERSION_NUMBER <= 0x10100000L
|
||||
td::bench(SHA1Bench());
|
||||
#endif
|
||||
td::bench(SHA1ShortBench());
|
||||
td::bench(SHA256ShortBench());
|
||||
td::bench(SHA512ShortBench());
|
||||
td::bench(HmacSha256ShortBench());
|
||||
td::bench(HmacSha512ShortBench());
|
||||
td::bench(Crc32Bench());
|
||||
td::bench(Crc64Bench());
|
||||
}
|
||||
244
td/benchmark/bench_db.cpp
Normal file
244
td/benchmark/bench_db.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/db/binlog/Binlog.h"
|
||||
#include "td/db/binlog/ConcurrentBinlog.h"
|
||||
#include "td/db/BinlogKeyValue.h"
|
||||
#include "td/db/DbKey.h"
|
||||
#include "td/db/SeqKeyValue.h"
|
||||
#include "td/db/SqliteConnectionSafe.h"
|
||||
#include "td/db/SqliteDb.h"
|
||||
#include "td/db/SqliteKeyValueAsync.h"
|
||||
#include "td/db/SqliteKeyValueSafe.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
template <class KeyValueT>
|
||||
class TdKvBench final : public td::Benchmark {
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
td::string name_;
|
||||
|
||||
public:
|
||||
explicit TdKvBench(td::string name) {
|
||||
name_ = std::move(name);
|
||||
}
|
||||
|
||||
td::string get_description() const final {
|
||||
return name_;
|
||||
}
|
||||
|
||||
class Main final : public td::Actor {
|
||||
public:
|
||||
explicit Main(int n) : n_(n) {
|
||||
}
|
||||
|
||||
private:
|
||||
void loop() final {
|
||||
KeyValueT::destroy("test_tddb").ignore();
|
||||
|
||||
class Worker final : public Actor {
|
||||
public:
|
||||
Worker(int n, td::string db_name) : n_(n) {
|
||||
kv_.init(db_name).ensure();
|
||||
}
|
||||
|
||||
private:
|
||||
void loop() final {
|
||||
for (int i = 0; i < n_; i++) {
|
||||
kv_.set(td::to_string(i % 10), td::to_string(i));
|
||||
}
|
||||
td::Scheduler::instance()->finish();
|
||||
}
|
||||
int n_;
|
||||
KeyValueT kv_;
|
||||
};
|
||||
td::create_actor_on_scheduler<Worker>("Worker", 0, n_, "test_tddb").release();
|
||||
}
|
||||
int n_;
|
||||
};
|
||||
|
||||
void start_up_n(int n) final {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(1, 0);
|
||||
scheduler_->create_actor_unsafe<Main>(1, "Main", n).release();
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
scheduler_->start();
|
||||
while (scheduler_->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler_->finish();
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
scheduler_.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <bool is_encrypted = false>
|
||||
class SqliteKVBench final : public td::Benchmark {
|
||||
td::SqliteDb db;
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "SqliteKV " << td::tag("is_encrypted", is_encrypted);
|
||||
}
|
||||
void start_up() final {
|
||||
td::string path = "testdb.sqlite";
|
||||
td::SqliteDb::destroy(path).ignore();
|
||||
if (is_encrypted) {
|
||||
db = td::SqliteDb::change_key(path, true, td::DbKey::password("cucumber"), td::DbKey::empty()).move_as_ok();
|
||||
} else {
|
||||
db = td::SqliteDb::open_with_key(path, true, td::DbKey::empty()).move_as_ok();
|
||||
}
|
||||
db.exec("PRAGMA encoding=\"UTF-8\"").ensure();
|
||||
db.exec("PRAGMA synchronous=NORMAL").ensure();
|
||||
db.exec("PRAGMA journal_mode=WAL").ensure();
|
||||
db.exec("PRAGMA temp_store=MEMORY").ensure();
|
||||
db.exec("DROP TABLE IF EXISTS KV").ensure();
|
||||
db.exec("CREATE TABLE IF NOT EXISTS KV (k BLOB PRIMARY KEY, v BLOB)").ensure();
|
||||
}
|
||||
void run(int n) final {
|
||||
auto stmt = db.get_statement("REPLACE INTO KV (k, v) VALUES(?1, ?2)").move_as_ok();
|
||||
db.exec("BEGIN TRANSACTION").ensure();
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto key = td::to_string(i % 10);
|
||||
auto value = td::to_string(i);
|
||||
stmt.bind_blob(1, key).ensure();
|
||||
stmt.bind_blob(2, value).ensure();
|
||||
stmt.step().ensure();
|
||||
CHECK(!stmt.can_step());
|
||||
stmt.reset();
|
||||
|
||||
if (i % 10 == 0) {
|
||||
db.exec("COMMIT TRANSACTION").ensure();
|
||||
db.exec("BEGIN TRANSACTION").ensure();
|
||||
}
|
||||
}
|
||||
db.exec("COMMIT TRANSACTION").ensure();
|
||||
}
|
||||
};
|
||||
|
||||
static td::Status init_db(td::SqliteDb &db) {
|
||||
TRY_STATUS(db.exec("PRAGMA encoding=\"UTF-8\""));
|
||||
TRY_STATUS(db.exec("PRAGMA journal_mode=WAL"));
|
||||
|
||||
TRY_STATUS(db.exec("PRAGMA synchronous=NORMAL"));
|
||||
TRY_STATUS(db.exec("PRAGMA temp_store=MEMORY"));
|
||||
// TRY_STATUS(db.exec("PRAGMA secure_delete=1"));
|
||||
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
class SqliteKeyValueAsyncBench final : public td::Benchmark {
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
return "SqliteKeyValueAsync";
|
||||
}
|
||||
void start_up() final {
|
||||
do_start_up().ensure();
|
||||
scheduler_->start();
|
||||
}
|
||||
void run(int n) final {
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto key = td::to_string(i % 10);
|
||||
auto value = td::to_string(i);
|
||||
sqlite_kv_async_->set(key, value, td::Auto());
|
||||
}
|
||||
}
|
||||
void tear_down() final {
|
||||
scheduler_->run_main(0.1);
|
||||
{
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
sqlite_kv_async_.reset();
|
||||
sqlite_kv_safe_.reset();
|
||||
sql_connection_->close_and_destroy();
|
||||
}
|
||||
|
||||
scheduler_->finish();
|
||||
scheduler_.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
std::shared_ptr<td::SqliteConnectionSafe> sql_connection_;
|
||||
std::shared_ptr<td::SqliteKeyValueSafe> sqlite_kv_safe_;
|
||||
td::unique_ptr<td::SqliteKeyValueAsyncInterface> sqlite_kv_async_;
|
||||
|
||||
td::Status do_start_up() {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(1, 0);
|
||||
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
|
||||
td::string sql_db_name = "testdb.sqlite";
|
||||
td::SqliteDb::destroy(sql_db_name).ignore();
|
||||
td::SqliteDb::open_with_key(sql_db_name, true, td::DbKey::empty()).move_as_ok();
|
||||
|
||||
sql_connection_ = std::make_shared<td::SqliteConnectionSafe>(sql_db_name, td::DbKey::empty());
|
||||
auto &db = sql_connection_->get();
|
||||
TRY_STATUS(init_db(db));
|
||||
|
||||
sqlite_kv_safe_ = std::make_shared<td::SqliteKeyValueSafe>("common", sql_connection_);
|
||||
sqlite_kv_async_ = create_sqlite_key_value_async(sqlite_kv_safe_, 0);
|
||||
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
class SeqKvBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return "SeqKvBench";
|
||||
}
|
||||
|
||||
td::SeqKeyValue kv;
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
kv.set(PSLICE() << i % 10, PSLICE() << i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <bool is_encrypted = false>
|
||||
class BinlogKeyValueBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "BinlogKeyValue " << td::tag("is_encrypted", is_encrypted);
|
||||
}
|
||||
|
||||
td::BinlogKeyValue<td::Binlog> kv;
|
||||
void start_up() final {
|
||||
td::SqliteDb::destroy("test_binlog").ignore();
|
||||
kv.init("test_binlog", is_encrypted ? td::DbKey::password("cucumber") : td::DbKey::empty()).ensure();
|
||||
}
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
kv.set(td::to_string(i % 10), td::to_string(i));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
|
||||
bench(TdKvBench<td::BinlogKeyValue<td::Binlog>>("BinlogKeyValue<Binlog>"));
|
||||
bench(TdKvBench<td::BinlogKeyValue<td::ConcurrentBinlog>>("BinlogKeyValue<ConcurrentBinlog>"));
|
||||
|
||||
bench(BinlogKeyValueBench<true>());
|
||||
bench(BinlogKeyValueBench<false>());
|
||||
bench(SqliteKVBench<false>());
|
||||
bench(SqliteKVBench<true>());
|
||||
bench(SqliteKeyValueAsyncBench());
|
||||
bench(SeqKvBench());
|
||||
}
|
||||
8
td/benchmark/bench_empty.cpp
Normal file
8
td/benchmark/bench_empty.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
int main() {
|
||||
}
|
||||
72
td/benchmark/bench_handshake.cpp
Normal file
72
td/benchmark/bench_handshake.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/mtproto/DhCallback.h"
|
||||
#include "td/mtproto/DhHandshake.h"
|
||||
|
||||
#include "td/utils/base64.h"
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
static td::int32 g = 3;
|
||||
static td::string prime_base64 =
|
||||
"xxyuucaxyQSObFIvcPE_c5gNQCOOPiHBSTTQN1Y9kw9IGYoKp8FAWCKUk9IlMPTb-jNvbgrJJROVQ67UTM58NyD9UfaUWHBaxozU_mtrE6vcl0ZRKW"
|
||||
"kyhFTxj6-MWV9kJHf-lrsqlB1bzR1KyMxJiAcI-ps3jjxPOpBgvuZ8-aSkppWBEFGQfhYnU7VrD2tBDbp02KhLKhSzFE4O8ShHVP0X7ZUNWWW0ud1G"
|
||||
"WC2xF40WnGvEZbDW_5yjko_vW5rk5Bj8Feg-vqD4f6n_Xu1wBQ3tKEn0e_lZ2VaFDOkphR8NgRX2NbEF7i5OFdBLJFS_b0-t8DSxBAMRnNjjuS_MW"
|
||||
"w";
|
||||
|
||||
class HandshakeBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return "Handshake";
|
||||
}
|
||||
|
||||
class FakeDhCallback final : public td::mtproto::DhCallback {
|
||||
public:
|
||||
int is_good_prime(td::Slice prime_str) const final {
|
||||
auto it = cache.find(prime_str.str());
|
||||
if (it == cache.end()) {
|
||||
return -1;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
void add_good_prime(td::Slice prime_str) const final {
|
||||
cache[prime_str.str()] = 1;
|
||||
}
|
||||
void add_bad_prime(td::Slice prime_str) const final {
|
||||
cache[prime_str.str()] = 0;
|
||||
}
|
||||
mutable std::map<td::string, int> cache;
|
||||
} dh_callback;
|
||||
|
||||
void run(int n) final {
|
||||
td::mtproto::DhHandshake a;
|
||||
td::mtproto::DhHandshake b;
|
||||
auto prime = td::base64url_decode(prime_base64).move_as_ok();
|
||||
td::mtproto::DhHandshake::check_config(g, prime, &dh_callback).ensure();
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
a.set_config(g, prime);
|
||||
b.set_config(g, prime);
|
||||
b.set_g_a(a.get_g_b());
|
||||
a.set_g_a(b.get_g_b());
|
||||
a.run_checks(true, &dh_callback).ensure();
|
||||
b.run_checks(true, &dh_callback).ensure();
|
||||
auto a_key = a.gen_key();
|
||||
auto b_key = b.gen_key();
|
||||
CHECK(a_key.first == b_key.first);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
td::bench(HandshakeBench());
|
||||
}
|
||||
77
td/benchmark/bench_http.cpp
Normal file
77
td/benchmark/bench_http.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/net/HttpOutboundConnection.h"
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/SslStream.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/BufferedFd.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
|
||||
std::atomic<int> counter;
|
||||
|
||||
class HttpClient final : public td::HttpOutboundConnection::Callback {
|
||||
void start_up() final {
|
||||
td::IPAddress addr;
|
||||
addr.init_ipv4_port("127.0.0.1", 8082).ensure();
|
||||
auto fd = td::SocketFd::open(addr);
|
||||
LOG_CHECK(fd.is_ok()) << fd.error();
|
||||
connection_ = td::create_actor<td::HttpOutboundConnection>(
|
||||
"Connect", td::BufferedFd<td::SocketFd>(fd.move_as_ok()), td::SslStream{}, std::numeric_limits<size_t>::max(),
|
||||
0, 0, td::ActorOwn<td::HttpOutboundConnection::Callback>(actor_id(this)));
|
||||
yield();
|
||||
cnt_ = 100000;
|
||||
counter++;
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
if (--counter == 0) {
|
||||
td::Scheduler::instance()->finish();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() final {
|
||||
if (cnt_-- < 0) {
|
||||
return stop();
|
||||
}
|
||||
send_closure(connection_, &td::HttpOutboundConnection::write_next, td::BufferSlice("GET / HTTP/1.1\r\n\r\n"));
|
||||
send_closure(connection_, &td::HttpOutboundConnection::write_ok);
|
||||
LOG(INFO) << "SEND";
|
||||
}
|
||||
|
||||
void handle(td::unique_ptr<td::HttpQuery> result) final {
|
||||
loop();
|
||||
}
|
||||
|
||||
void on_connection_error(td::Status error) final {
|
||||
LOG(ERROR) << "ERROR: " << error;
|
||||
}
|
||||
|
||||
td::ActorOwn<td::HttpOutboundConnection> connection_;
|
||||
int cnt_ = 0;
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>(0, 0);
|
||||
scheduler->create_actor_unsafe<HttpClient>(0, "Client1").release();
|
||||
scheduler->create_actor_unsafe<HttpClient>(0, "Client2").release();
|
||||
scheduler->start();
|
||||
while (scheduler->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler->finish();
|
||||
}
|
||||
119
td/benchmark/bench_http_reader.cpp
Normal file
119
td/benchmark/bench_http_reader.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/HttpReader.h"
|
||||
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/find_boundary.h"
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
static std::string http_query = "GET / HTTP/1.1\r\nConnection:keep-alive\r\nhost:127.0.0.1:8080\r\n\r\n";
|
||||
static const size_t block_size = 2500;
|
||||
|
||||
class HttpReaderBench final : public td::Benchmark {
|
||||
std::string get_description() const final {
|
||||
return "HttpReaderBench";
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
auto cnt = static_cast<int>(block_size / http_query.size());
|
||||
td::HttpQuery q;
|
||||
int parsed = 0;
|
||||
int sent = 0;
|
||||
for (int i = 0; i < n; i += cnt) {
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
writer_.append(http_query);
|
||||
sent++;
|
||||
}
|
||||
reader_.sync_with_writer();
|
||||
while (true) {
|
||||
auto wait = http_reader_.read_next(&q).ok();
|
||||
if (wait != 0) {
|
||||
break;
|
||||
}
|
||||
parsed++;
|
||||
}
|
||||
}
|
||||
CHECK(parsed == sent);
|
||||
}
|
||||
td::ChainBufferWriter writer_;
|
||||
td::ChainBufferReader reader_;
|
||||
td::HttpReader http_reader_;
|
||||
|
||||
void start_up() final {
|
||||
writer_ = {};
|
||||
reader_ = writer_.extract_reader();
|
||||
http_reader_.init(&reader_, 10000, 0);
|
||||
}
|
||||
};
|
||||
|
||||
class BufferBench final : public td::Benchmark {
|
||||
std::string get_description() const final {
|
||||
return "BufferBench";
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
auto cnt = static_cast<int>(block_size / http_query.size());
|
||||
for (int i = 0; i < n; i += cnt) {
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
writer_.append(http_query);
|
||||
}
|
||||
reader_.sync_with_writer();
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
auto result = reader_.cut_head(http_query.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
td::ChainBufferWriter writer_;
|
||||
td::ChainBufferReader reader_;
|
||||
td::HttpReader http_reader_;
|
||||
|
||||
void start_up() final {
|
||||
writer_ = {};
|
||||
reader_ = writer_.extract_reader();
|
||||
}
|
||||
};
|
||||
|
||||
class FindBoundaryBench final : public td::Benchmark {
|
||||
std::string get_description() const final {
|
||||
return "FindBoundaryBench";
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
auto cnt = static_cast<int>(block_size / http_query.size());
|
||||
for (int i = 0; i < n; i += cnt) {
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
writer_.append(http_query);
|
||||
}
|
||||
reader_.sync_with_writer();
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
size_t len = 0;
|
||||
find_boundary(reader_.clone(), "\r\n\r\n", len);
|
||||
CHECK(size_t(len) + 4 == http_query.size());
|
||||
auto result = reader_.cut_head(len + 2);
|
||||
reader_.advance(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
td::ChainBufferWriter writer_;
|
||||
td::ChainBufferReader reader_;
|
||||
td::HttpReader http_reader_;
|
||||
|
||||
void start_up() final {
|
||||
writer_ = {};
|
||||
reader_ = writer_.extract_reader();
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
|
||||
td::bench(BufferBench());
|
||||
td::bench(FindBoundaryBench());
|
||||
td::bench(HttpReaderBench());
|
||||
}
|
||||
84
td/benchmark/bench_http_server.cpp
Normal file
84
td/benchmark/bench_http_server.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/net/HttpHeaderCreator.h"
|
||||
#include "td/net/HttpInboundConnection.h"
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/TcpListener.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/BufferedFd.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
static int cnt = 0;
|
||||
|
||||
class HelloWorld final : public td::HttpInboundConnection::Callback {
|
||||
public:
|
||||
void handle(td::unique_ptr<td::HttpQuery> query, td::ActorOwn<td::HttpInboundConnection> connection) final {
|
||||
// LOG(ERROR) << *query;
|
||||
td::HttpHeaderCreator hc;
|
||||
td::Slice content = "hello world";
|
||||
//auto content = td::BufferSlice("hello world");
|
||||
hc.init_ok();
|
||||
hc.set_keep_alive();
|
||||
hc.set_content_size(content.size());
|
||||
hc.add_header("Server", "TDLib/test");
|
||||
hc.add_header("Date", "Thu Dec 14 01:41:50 2017");
|
||||
hc.add_header("Content-Type:", "text/html");
|
||||
|
||||
auto res = hc.finish(content);
|
||||
LOG_IF(FATAL, res.is_error()) << res.error();
|
||||
send_closure(connection, &td::HttpInboundConnection::write_next, td::BufferSlice(res.ok()));
|
||||
send_closure(connection.release(), &td::HttpInboundConnection::write_ok);
|
||||
}
|
||||
void hangup() final {
|
||||
LOG(ERROR) << "CLOSE " << cnt--;
|
||||
stop();
|
||||
}
|
||||
};
|
||||
|
||||
const int N = 0;
|
||||
class Server final : public td::TcpListener::Callback {
|
||||
public:
|
||||
void start_up() final {
|
||||
listener_ =
|
||||
td::create_actor<td::TcpListener>("Listener", 8082, td::ActorOwn<td::TcpListener::Callback>(actor_id(this)));
|
||||
}
|
||||
void accept(td::SocketFd fd) final {
|
||||
LOG(ERROR) << "ACCEPT " << cnt++;
|
||||
pos_++;
|
||||
auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
|
||||
td::create_actor_on_scheduler<td::HttpInboundConnection>(
|
||||
"HttpInboundConnection", scheduler_id, td::BufferedFd<td::SocketFd>(std::move(fd)), 1024 * 1024, 0, 0,
|
||||
td::create_actor_on_scheduler<HelloWorld>("HelloWorld", scheduler_id))
|
||||
.release();
|
||||
}
|
||||
void hangup() final {
|
||||
// may be it should be default?..
|
||||
LOG(ERROR) << "Hanging up..";
|
||||
stop();
|
||||
}
|
||||
|
||||
private:
|
||||
td::ActorOwn<td::TcpListener> listener_;
|
||||
int pos_{0};
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>(N, 0);
|
||||
scheduler->create_actor_unsafe<Server>(0, "Server").release();
|
||||
scheduler->start();
|
||||
while (scheduler->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler->finish();
|
||||
}
|
||||
132
td/benchmark/bench_http_server_cheat.cpp
Normal file
132
td/benchmark/bench_http_server_cheat.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/net/HttpHeaderCreator.h"
|
||||
#include "td/net/TcpListener.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/detail/PollableFd.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
static int cnt = 0;
|
||||
|
||||
class HelloWorld final : public td::Actor {
|
||||
public:
|
||||
explicit HelloWorld(td::SocketFd socket_fd) : socket_fd_(std::move(socket_fd)) {
|
||||
}
|
||||
|
||||
private:
|
||||
td::SocketFd socket_fd_;
|
||||
|
||||
std::array<char, 1024> read_buf;
|
||||
size_t read_new_lines{0};
|
||||
|
||||
td::string hello_;
|
||||
td::string write_buf_;
|
||||
size_t write_pos_{0};
|
||||
|
||||
void start_up() final {
|
||||
td::Scheduler::subscribe(socket_fd_.get_poll_info().extract_pollable_fd(this));
|
||||
td::HttpHeaderCreator hc;
|
||||
td::Slice content = "hello world";
|
||||
//auto content = td::BufferSlice("hello world");
|
||||
hc.init_ok();
|
||||
hc.set_keep_alive();
|
||||
hc.set_content_size(content.size());
|
||||
hc.add_header("Server", "TDLib/test");
|
||||
hc.add_header("Date", "Thu Dec 14 01:41:50 2017");
|
||||
hc.add_header("Content-Type:", "text/html");
|
||||
hello_ = hc.finish(content).ok().str();
|
||||
}
|
||||
|
||||
void loop() final {
|
||||
auto status = do_loop();
|
||||
if (status.is_error()) {
|
||||
td::Scheduler::unsubscribe(socket_fd_.get_poll_info().get_pollable_fd_ref());
|
||||
stop();
|
||||
LOG(ERROR) << "CLOSE: " << status;
|
||||
}
|
||||
}
|
||||
td::Status do_loop() {
|
||||
sync_with_poll(socket_fd_);
|
||||
TRY_STATUS(read_loop());
|
||||
TRY_STATUS(write_loop());
|
||||
if (can_close_local(socket_fd_)) {
|
||||
return td::Status::Error("CLOSE");
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
td::Status write_loop() {
|
||||
while (can_write_local(socket_fd_) && write_pos_ < write_buf_.size()) {
|
||||
TRY_RESULT(written, socket_fd_.write(td::Slice(write_buf_).substr(write_pos_)));
|
||||
write_pos_ += written;
|
||||
if (write_pos_ == write_buf_.size()) {
|
||||
write_pos_ = 0;
|
||||
write_buf_.clear();
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
td::Status read_loop() {
|
||||
while (can_read_local(socket_fd_)) {
|
||||
TRY_RESULT(read_size, socket_fd_.read(td::MutableSlice(read_buf.data(), read_buf.size())));
|
||||
for (size_t i = 0; i < read_size; i++) {
|
||||
if (read_buf[i] == '\n') {
|
||||
read_new_lines++;
|
||||
if (read_new_lines == 2) {
|
||||
read_new_lines = 0;
|
||||
write_buf_.append(hello_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
const int N = 0;
|
||||
class Server final : public td::TcpListener::Callback {
|
||||
public:
|
||||
void start_up() final {
|
||||
listener_ =
|
||||
td::create_actor<td::TcpListener>("Listener", 8082, td::ActorOwn<td::TcpListener::Callback>(actor_id(this)));
|
||||
}
|
||||
void accept(td::SocketFd fd) final {
|
||||
LOG(ERROR) << "ACCEPT " << cnt++;
|
||||
pos_++;
|
||||
auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
|
||||
td::create_actor_on_scheduler<HelloWorld>("HelloWorld", scheduler_id, std::move(fd)).release();
|
||||
}
|
||||
void hangup() final {
|
||||
// may be it should be default?..
|
||||
LOG(ERROR) << "Hanging up..";
|
||||
stop();
|
||||
}
|
||||
|
||||
private:
|
||||
td::ActorOwn<td::TcpListener> listener_;
|
||||
int pos_{0};
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>(N, 0);
|
||||
scheduler->create_actor_unsafe<Server>(0, "Server").release();
|
||||
scheduler->start();
|
||||
while (scheduler->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler->finish();
|
||||
}
|
||||
116
td/benchmark/bench_http_server_fast.cpp
Normal file
116
td/benchmark/bench_http_server_fast.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/net/HttpHeaderCreator.h"
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/HttpReader.h"
|
||||
#include "td/net/TcpListener.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/BufferedFd.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/detail/PollableFd.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
class HttpEchoConnection final : public td::Actor {
|
||||
public:
|
||||
explicit HttpEchoConnection(td::SocketFd fd) : fd_(std::move(fd)) {
|
||||
}
|
||||
|
||||
private:
|
||||
td::BufferedFd<td::SocketFd> fd_;
|
||||
td::HttpReader reader_;
|
||||
td::HttpQuery query_;
|
||||
void start_up() final {
|
||||
td::Scheduler::subscribe(fd_.get_poll_info().extract_pollable_fd(this));
|
||||
reader_.init(&fd_.input_buffer(), 1024 * 1024, 0);
|
||||
}
|
||||
void tear_down() final {
|
||||
td::Scheduler::unsubscribe_before_close(fd_.get_poll_info().get_pollable_fd_ref());
|
||||
fd_.close();
|
||||
}
|
||||
|
||||
void handle_query() {
|
||||
query_ = td::HttpQuery();
|
||||
td::HttpHeaderCreator hc;
|
||||
td::Slice content = "hello world";
|
||||
//auto content = td::BufferSlice("hello world");
|
||||
hc.init_ok();
|
||||
hc.set_keep_alive();
|
||||
hc.set_content_size(content.size());
|
||||
hc.add_header("Server", "TDLib/test");
|
||||
hc.add_header("Date", "Thu Dec 14 01:41:50 2017");
|
||||
hc.add_header("Content-Type:", "text/html");
|
||||
auto res = hc.finish(content);
|
||||
fd_.output_buffer().append(res.ok());
|
||||
}
|
||||
|
||||
void loop() final {
|
||||
sync_with_poll(fd_);
|
||||
auto status = [&] {
|
||||
TRY_STATUS(loop_read());
|
||||
TRY_STATUS(loop_write());
|
||||
return td::Status::OK();
|
||||
}();
|
||||
if (status.is_error() || can_close_local(fd_)) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
td::Status loop_read() {
|
||||
TRY_STATUS(fd_.flush_read());
|
||||
while (true) {
|
||||
TRY_RESULT(need, reader_.read_next(&query_));
|
||||
if (need == 0) {
|
||||
handle_query();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
td::Status loop_write() {
|
||||
TRY_STATUS(fd_.flush_write());
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
const int N = 8;
|
||||
class Server final : public td::TcpListener::Callback {
|
||||
public:
|
||||
void start_up() final {
|
||||
listener_ =
|
||||
td::create_actor<td::TcpListener>("Listener", 8082, td::ActorOwn<td::TcpListener::Callback>(actor_id(this)));
|
||||
}
|
||||
void accept(td::SocketFd fd) final {
|
||||
pos_++;
|
||||
auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
|
||||
td::create_actor_on_scheduler<HttpEchoConnection>("HttpEchoConnection", scheduler_id, std::move(fd)).release();
|
||||
}
|
||||
void hangup() final {
|
||||
LOG(ERROR) << "Hanging up..";
|
||||
stop();
|
||||
}
|
||||
|
||||
private:
|
||||
td::ActorOwn<td::TcpListener> listener_;
|
||||
int pos_{0};
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>(N, 0);
|
||||
scheduler->create_actor_unsafe<Server>(0, "Server").release();
|
||||
scheduler->start();
|
||||
while (scheduler->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler->finish();
|
||||
}
|
||||
160
td/benchmark/bench_log.cpp
Normal file
160
td/benchmark/bench_log.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
std::string create_tmp_file() {
|
||||
#if TD_ANDROID
|
||||
std::string name = "/data/local/tmp/large_file.txt";
|
||||
unlink(name.c_str());
|
||||
return name;
|
||||
#else
|
||||
char file_name[] = "largefileXXXXXX";
|
||||
int fd = mkstemp(file_name);
|
||||
if (fd == -1) {
|
||||
perror("Can't cretate temporary file");
|
||||
}
|
||||
CHECK(fd != -1);
|
||||
|
||||
close(fd);
|
||||
return file_name;
|
||||
#endif
|
||||
}
|
||||
|
||||
class IostreamWriteBench final : public td::Benchmark {
|
||||
protected:
|
||||
std::string file_name_;
|
||||
std::ofstream stream;
|
||||
static constexpr std::size_t BUFFER_SIZE = 1 << 20;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
public:
|
||||
std::string get_description() const final {
|
||||
return "ostream (to file, no buf, no flush)";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
file_name_ = create_tmp_file();
|
||||
stream.open(file_name_.c_str());
|
||||
CHECK(stream.is_open());
|
||||
// stream.rdbuf()->pubsetbuf(buffer, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
stream << "This is just for test" << 987654321 << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
stream.close();
|
||||
unlink(file_name_.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class FILEWriteBench final : public td::Benchmark {
|
||||
protected:
|
||||
std::string file_name_;
|
||||
FILE *file;
|
||||
static constexpr std::size_t BUFFER_SIZE = 1 << 20;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
public:
|
||||
std::string get_description() const final {
|
||||
return "std::fprintf (to file, no buf, no flush)";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
file_name_ = create_tmp_file();
|
||||
file = fopen(file_name_.c_str(), "w");
|
||||
// setvbuf(file, buffer, _IOFBF, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
std::fprintf(file, "This is just for test%d\n", 987654321);
|
||||
// std::fflush(file);
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
std::fclose(file);
|
||||
unlink(file_name_.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
#if TD_ANDROID
|
||||
#include <android/log.h>
|
||||
#define ALOG(...) __android_log_print(ANDROID_LOG_VERBOSE, "XXX", __VA_ARGS__)
|
||||
class ALogWriteBench final : public td::Benchmark {
|
||||
public:
|
||||
std::string get_description() const final {
|
||||
return "android_log";
|
||||
}
|
||||
void start_up() final {
|
||||
}
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
ALOG("This is just for test%d\n", 987654321);
|
||||
}
|
||||
}
|
||||
void tear_down() final {
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class LogWriteBench final : public td::Benchmark {
|
||||
protected:
|
||||
std::string file_name_;
|
||||
std::ofstream stream;
|
||||
std::streambuf *old_buf;
|
||||
static constexpr std::size_t BUFFER_SIZE = 1 << 20;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
public:
|
||||
std::string get_description() const final {
|
||||
return "td_log (slow in debug mode)";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
file_name_ = create_tmp_file();
|
||||
stream.open(file_name_.c_str());
|
||||
CHECK(stream.is_open());
|
||||
old_buf = std::cerr.rdbuf(stream.rdbuf());
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
LOG(DEBUG) << "This is just for test" << 987654321;
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
stream.close();
|
||||
unlink(file_name_.c_str());
|
||||
std::cerr.rdbuf(old_buf);
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
td::bench(LogWriteBench());
|
||||
#if TD_ANDROID
|
||||
td::bench(ALogWriteBench());
|
||||
#endif
|
||||
td::bench(IostreamWriteBench());
|
||||
td::bench(FILEWriteBench());
|
||||
}
|
||||
841
td/benchmark/bench_misc.cpp
Normal file
841
td/benchmark/bench_misc.cpp
Normal file
@@ -0,0 +1,841 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/telegram/td_api.h"
|
||||
#include "td/telegram/telegram_api.h"
|
||||
#include "td/telegram/telegram_api.hpp"
|
||||
|
||||
#include "td/utils/algorithm.h"
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/Clocks.h"
|
||||
#include "td/utils/port/EventFd.h"
|
||||
#include "td/utils/port/FileFd.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/port/RwMutex.h"
|
||||
#include "td/utils/port/Stat.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/StackAllocator.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/ThreadSafeCounter.h"
|
||||
|
||||
#if !TD_WINDOWS
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
#endif
|
||||
|
||||
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
|
||||
class F {
|
||||
td::uint32 ∑
|
||||
|
||||
public:
|
||||
explicit F(td::uint32 &sum) : sum(sum) {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void operator()(const T &x) const {
|
||||
sum += static_cast<td::uint32>(reinterpret_cast<std::uintptr_t>(&x));
|
||||
}
|
||||
};
|
||||
|
||||
BENCH(TlCall, "TL Call") {
|
||||
td::tl_object_ptr<td::telegram_api::Function> x = td::make_tl_object<td::telegram_api::account_getWallPapers>(0);
|
||||
td::uint32 res = 0;
|
||||
F f(res);
|
||||
for (int i = 0; i < n; i++) {
|
||||
downcast_call(*x, f);
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
static td::td_api::object_ptr<td::td_api::file> get_file_object() {
|
||||
return td::td_api::make_object<td::td_api::file>(
|
||||
12345, 123456, 123456,
|
||||
td::td_api::make_object<td::td_api::localFile>(
|
||||
"/android/data/0/data/org.telegram.data/files/photos/12345678901234567890_123.jpg", true, true, false, true,
|
||||
0, 123456, 123456),
|
||||
td::td_api::make_object<td::td_api::remoteFile>("abacabadabacabaeabacabadabacabafabacabadabacabaeabacabadabacaba",
|
||||
"abacabadabacabaeabacabadabacaba", false, true, 123456));
|
||||
}
|
||||
|
||||
BENCH(ToStringIntSmall, "to_string<int> small") {
|
||||
auto buf = td::StackAllocator::alloc(1000);
|
||||
td::StringBuilder sb(buf.as_slice());
|
||||
for (int i = 0; i < n; i++) {
|
||||
sb << td::Random::fast(0, 100);
|
||||
sb.clear();
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(ToStringIntBig, "to_string<int> big") {
|
||||
auto buf = td::StackAllocator::alloc(1000);
|
||||
td::StringBuilder sb(buf.as_slice());
|
||||
for (int i = 0; i < n; i++) {
|
||||
sb << 1234567890;
|
||||
sb.clear();
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(TlToStringUpdateFile, "TL to_string updateFile") {
|
||||
auto x = td::td_api::make_object<td::td_api::updateFile>(get_file_object());
|
||||
|
||||
std::size_t res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += to_string(x).size();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(TlToStringMessage, "TL to_string message") {
|
||||
auto x = td::td_api::make_object<td::td_api::message>();
|
||||
x->id_ = 123456000111;
|
||||
x->sender_id_ = td::td_api::make_object<td::td_api::messageSenderUser>(123456000112);
|
||||
x->chat_id_ = 123456000112;
|
||||
x->sending_state_ = td::td_api::make_object<td::td_api::messageSendingStatePending>(0);
|
||||
x->date_ = 1699999999;
|
||||
auto photo = td::td_api::make_object<td::td_api::photo>();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
photo->sizes_.push_back(td::td_api::make_object<td::td_api::photoSize>(
|
||||
"a", get_file_object(), 160, 160,
|
||||
td::vector<td::int32>{10000, 20000, 30000, 50000, 70000, 90000, 120000, 150000, 180000, 220000}));
|
||||
}
|
||||
x->content_ = td::td_api::make_object<td::td_api::messagePhoto>(
|
||||
std::move(photo), td::td_api::make_object<td::td_api::formattedText>(), false, false, false);
|
||||
|
||||
std::size_t res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += to_string(x).size();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
#if !TD_EVENTFD_UNSUPPORTED
|
||||
BENCH(EventFd, "EventFd") {
|
||||
td::EventFd fd;
|
||||
fd.init();
|
||||
for (int i = 0; i < n; i++) {
|
||||
fd.release();
|
||||
fd.acquire();
|
||||
}
|
||||
fd.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
BENCH(NewInt, "new int + delete") {
|
||||
std::uintptr_t res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int *x = new int;
|
||||
res += reinterpret_cast<std::uintptr_t>(x);
|
||||
delete x;
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(NewObj, "new struct, then delete") {
|
||||
struct A {
|
||||
td::int32 a = 0;
|
||||
td::int32 b = 0;
|
||||
td::int32 c = 0;
|
||||
td::int32 d = 0;
|
||||
};
|
||||
std::uintptr_t res = 0;
|
||||
A **ptr = new A *[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
ptr[i] = new A();
|
||||
res += reinterpret_cast<std::uintptr_t>(ptr[i]);
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
delete ptr[i];
|
||||
}
|
||||
delete[] ptr;
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
BENCH(ThreadNew, "new struct, then delete in 2 threads") {
|
||||
NewObjBench a;
|
||||
NewObjBench b;
|
||||
td::thread ta([&] { a.run(n / 2); });
|
||||
td::thread tb([&] { b.run(n - n / 2); });
|
||||
ta.join();
|
||||
tb.join();
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
// Too hard for clang (?)
|
||||
BENCH(Time, "Clocks::monotonic") {
|
||||
double res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += td::Clocks::monotonic();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
*/
|
||||
#if !TD_WINDOWS
|
||||
class PipeBench final : public td::Benchmark {
|
||||
public:
|
||||
int p[2];
|
||||
|
||||
td::string get_description() const final {
|
||||
return "pipe write + read int32";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
int res = pipe(p);
|
||||
CHECK(res == 0);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int val = 1;
|
||||
auto write_len = write(p[1], &val, sizeof(val));
|
||||
CHECK(write_len == sizeof(val));
|
||||
auto read_len = read(p[0], &val, sizeof(val));
|
||||
CHECK(read_len == sizeof(val));
|
||||
res += val;
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
||||
class SemBench final : public td::Benchmark {
|
||||
sem_t sem;
|
||||
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
return "sem post + wait";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
int err = sem_init(&sem, 0, 0);
|
||||
CHECK(err != -1);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
sem_post(&sem);
|
||||
sem_wait(&sem);
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
sem_destroy(&sem);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !TD_WINDOWS
|
||||
class UtimeBench final : public td::Benchmark {
|
||||
public:
|
||||
void start_up() final {
|
||||
td::FileFd::open("test", td::FileFd::Create | td::FileFd::Write).move_as_ok().close();
|
||||
}
|
||||
td::string get_description() const final {
|
||||
return "utime";
|
||||
}
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
int err = utime("test", nullptr);
|
||||
CHECK(err >= 0);
|
||||
utimbuf buf;
|
||||
buf.modtime = 123;
|
||||
buf.actime = 321;
|
||||
err = utime("test", &buf);
|
||||
CHECK(err >= 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
BENCH(Pwrite, "pwrite") {
|
||||
auto fd = td::FileFd::open("test", td::FileFd::Create | td::FileFd::Write).move_as_ok();
|
||||
for (int i = 0; i < n; i++) {
|
||||
fd.pwrite("a", 0).ok();
|
||||
}
|
||||
fd.close();
|
||||
}
|
||||
|
||||
class CreateFileBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return "create_file";
|
||||
}
|
||||
void start_up() final {
|
||||
td::mkdir("A").ensure();
|
||||
}
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::FileFd::open(PSLICE() << "A/" << i, td::FileFd::Write | td::FileFd::Create).move_as_ok().close();
|
||||
}
|
||||
}
|
||||
void tear_down() final {
|
||||
td::rmrf("A/").ignore();
|
||||
}
|
||||
};
|
||||
|
||||
class WalkPathBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return "walk_path";
|
||||
}
|
||||
void start_up_n(int n) final {
|
||||
td::mkdir("A").ensure();
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::FileFd::open(PSLICE() << "A/" << i, td::FileFd::Write | td::FileFd::Create).move_as_ok().close();
|
||||
}
|
||||
}
|
||||
void run(int n) final {
|
||||
int cnt = 0;
|
||||
td::walk_path("A/", [&](td::CSlice path, auto type) {
|
||||
if (type == td::WalkPath::Type::EnterDir) {
|
||||
return;
|
||||
}
|
||||
td::stat(path).ok();
|
||||
cnt++;
|
||||
}).ignore();
|
||||
}
|
||||
void tear_down() final {
|
||||
td::rmrf("A/").ignore();
|
||||
}
|
||||
};
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
template <int ThreadN = 2>
|
||||
class AtomicReleaseIncBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "AtomicReleaseInc" << ThreadN;
|
||||
}
|
||||
|
||||
static std::atomic<td::uint64> a_;
|
||||
void run(int n) final {
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < ThreadN; i++) {
|
||||
threads.emplace_back([&] {
|
||||
for (int i = 0; i < n / ThreadN; i++) {
|
||||
a_.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
template <int ThreadN>
|
||||
std::atomic<td::uint64> AtomicReleaseIncBench<ThreadN>::a_;
|
||||
|
||||
template <int ThreadN = 2>
|
||||
class AtomicReleaseCasIncBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "AtomicReleaseCasInc" << ThreadN;
|
||||
}
|
||||
|
||||
static std::atomic<td::uint64> a_;
|
||||
void run(int n) final {
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < ThreadN; i++) {
|
||||
threads.emplace_back([&] {
|
||||
for (int i = 0; i < n / ThreadN; i++) {
|
||||
auto value = a_.load(std::memory_order_relaxed);
|
||||
while (!a_.compare_exchange_strong(value, value + 1, std::memory_order_release, std::memory_order_relaxed)) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
template <int ThreadN>
|
||||
std::atomic<td::uint64> AtomicReleaseCasIncBench<ThreadN>::a_;
|
||||
|
||||
template <int ThreadN>
|
||||
class RwMutexReadBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "RwMutexRead" << ThreadN;
|
||||
}
|
||||
td::RwMutex mutex_;
|
||||
void run(int n) final {
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < ThreadN; i++) {
|
||||
threads.emplace_back([&] {
|
||||
for (int i = 0; i < n / ThreadN; i++) {
|
||||
mutex_.lock_read().ensure();
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <int ThreadN>
|
||||
class RwMutexWriteBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "RwMutexWrite" << ThreadN;
|
||||
}
|
||||
td::RwMutex mutex_;
|
||||
void run(int n) final {
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < ThreadN; i++) {
|
||||
threads.emplace_back([&] {
|
||||
for (int i = 0; i < n / ThreadN; i++) {
|
||||
mutex_.lock_write().ensure();
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ThreadSafeCounterBench final : public td::Benchmark {
|
||||
static td::ThreadSafeCounter counter_;
|
||||
int thread_count_;
|
||||
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "ThreadSafeCounter" << thread_count_;
|
||||
}
|
||||
void run(int n) final {
|
||||
counter_.clear();
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < thread_count_; i++) {
|
||||
threads.emplace_back([n] {
|
||||
for (int i = 0; i < n; i++) {
|
||||
counter_.add(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
CHECK(counter_.sum() == n * thread_count_);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ThreadSafeCounterBench(int thread_count) : thread_count_(thread_count) {
|
||||
}
|
||||
};
|
||||
td::ThreadSafeCounter ThreadSafeCounterBench::counter_;
|
||||
|
||||
template <bool StrictOrder>
|
||||
class AtomicCounterBench final : public td::Benchmark {
|
||||
static std::atomic<td::int64> counter_;
|
||||
int thread_count_;
|
||||
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "AtomicCounter" << thread_count_;
|
||||
}
|
||||
void run(int n) final {
|
||||
counter_.store(0);
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < thread_count_; i++) {
|
||||
threads.emplace_back([n] {
|
||||
for (int i = 0; i < n; i++) {
|
||||
counter_.fetch_add(1, StrictOrder ? std::memory_order_seq_cst : std::memory_order_relaxed);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
CHECK(counter_.load() == n * thread_count_);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit AtomicCounterBench(int thread_count) : thread_count_(thread_count) {
|
||||
}
|
||||
};
|
||||
template <bool StrictOrder>
|
||||
std::atomic<td::int64> AtomicCounterBench<StrictOrder>::counter_;
|
||||
|
||||
#endif
|
||||
|
||||
class IdDuplicateCheckerOld {
|
||||
public:
|
||||
static td::string get_description() {
|
||||
return "Old";
|
||||
}
|
||||
td::Status check(td::uint64 message_id) {
|
||||
if (saved_message_ids_.size() == MAX_SAVED_MESSAGE_IDS) {
|
||||
auto oldest_message_id = *saved_message_ids_.begin();
|
||||
if (message_id < oldest_message_id) {
|
||||
return td::Status::Error(2, PSLICE() << "Ignore very old message " << message_id
|
||||
<< " older than the oldest known message " << oldest_message_id);
|
||||
}
|
||||
}
|
||||
if (saved_message_ids_.count(message_id) != 0) {
|
||||
return td::Status::Error(1, PSLICE() << "Ignore already processed message " << message_id);
|
||||
}
|
||||
|
||||
saved_message_ids_.insert(message_id);
|
||||
if (saved_message_ids_.size() > MAX_SAVED_MESSAGE_IDS) {
|
||||
saved_message_ids_.erase(saved_message_ids_.begin());
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_SAVED_MESSAGE_IDS = 1000;
|
||||
std::set<td::uint64> saved_message_ids_;
|
||||
};
|
||||
|
||||
template <size_t MAX_SAVED_MESSAGE_IDS>
|
||||
class IdDuplicateCheckerNew {
|
||||
public:
|
||||
static td::string get_description() {
|
||||
return PSTRING() << "New" << MAX_SAVED_MESSAGE_IDS;
|
||||
}
|
||||
td::Status check(td::uint64 message_id) {
|
||||
auto insert_result = saved_message_ids_.insert(message_id);
|
||||
if (!insert_result.second) {
|
||||
return td::Status::Error(1, PSLICE() << "Ignore already processed message " << message_id);
|
||||
}
|
||||
if (saved_message_ids_.size() == MAX_SAVED_MESSAGE_IDS + 1) {
|
||||
auto begin_it = saved_message_ids_.begin();
|
||||
bool is_very_old = begin_it == insert_result.first;
|
||||
saved_message_ids_.erase(begin_it);
|
||||
if (is_very_old) {
|
||||
return td::Status::Error(2, PSLICE() << "Ignore very old message " << message_id
|
||||
<< " older than the oldest known message " << *saved_message_ids_.begin());
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<td::uint64> saved_message_ids_;
|
||||
};
|
||||
|
||||
class IdDuplicateCheckerNewOther {
|
||||
public:
|
||||
static td::string get_description() {
|
||||
return "NewOther";
|
||||
}
|
||||
td::Status check(td::uint64 message_id) {
|
||||
if (!saved_message_ids_.insert(message_id).second) {
|
||||
return td::Status::Error(1, PSLICE() << "Ignore already processed message " << message_id);
|
||||
}
|
||||
if (saved_message_ids_.size() == MAX_SAVED_MESSAGE_IDS + 1) {
|
||||
auto begin_it = saved_message_ids_.begin();
|
||||
bool is_very_old = *begin_it == message_id;
|
||||
saved_message_ids_.erase(begin_it);
|
||||
if (is_very_old) {
|
||||
return td::Status::Error(2, PSLICE() << "Ignore very old message " << message_id
|
||||
<< " older than the oldest known message " << *saved_message_ids_.begin());
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_SAVED_MESSAGE_IDS = 1000;
|
||||
std::set<td::uint64> saved_message_ids_;
|
||||
};
|
||||
|
||||
class IdDuplicateCheckerNewSimple {
|
||||
public:
|
||||
static td::string get_description() {
|
||||
return "NewSimple";
|
||||
}
|
||||
td::Status check(td::uint64 message_id) {
|
||||
auto insert_result = saved_message_ids_.insert(message_id);
|
||||
if (!insert_result.second) {
|
||||
return td::Status::Error(1, "Ignore already processed message");
|
||||
}
|
||||
if (saved_message_ids_.size() == MAX_SAVED_MESSAGE_IDS + 1) {
|
||||
auto begin_it = saved_message_ids_.begin();
|
||||
bool is_very_old = begin_it == insert_result.first;
|
||||
saved_message_ids_.erase(begin_it);
|
||||
if (is_very_old) {
|
||||
return td::Status::Error(2, "Ignore very old message");
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_SAVED_MESSAGE_IDS = 1000;
|
||||
std::set<td::uint64> saved_message_ids_;
|
||||
};
|
||||
|
||||
template <size_t max_size>
|
||||
class IdDuplicateCheckerArray {
|
||||
public:
|
||||
static td::string get_description() {
|
||||
return PSTRING() << "Array" << max_size;
|
||||
}
|
||||
td::Status check(td::uint64 message_id) {
|
||||
if (end_pos_ == 2 * max_size) {
|
||||
std::copy_n(&saved_message_ids_[max_size], max_size, &saved_message_ids_[0]);
|
||||
end_pos_ = max_size;
|
||||
}
|
||||
if (end_pos_ == 0 || message_id > saved_message_ids_[end_pos_ - 1]) {
|
||||
// fast path
|
||||
saved_message_ids_[end_pos_++] = message_id;
|
||||
return td::Status::OK();
|
||||
}
|
||||
if (end_pos_ >= max_size && message_id < saved_message_ids_[0]) {
|
||||
return td::Status::Error(2, PSLICE() << "Ignore very old message " << message_id
|
||||
<< " older than the oldest known message " << saved_message_ids_[0]);
|
||||
}
|
||||
auto it = std::lower_bound(&saved_message_ids_[0], &saved_message_ids_[end_pos_], message_id);
|
||||
if (*it == message_id) {
|
||||
return td::Status::Error(1, PSLICE() << "Ignore already processed message " << message_id);
|
||||
}
|
||||
std::copy_backward(it, &saved_message_ids_[end_pos_], &saved_message_ids_[end_pos_ + 1]);
|
||||
*it = message_id;
|
||||
++end_pos_;
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<td::uint64, 2 * max_size> saved_message_ids_;
|
||||
std::size_t end_pos_ = 0;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DuplicateCheckerBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "DuplicateCheckerBench" << T::get_description();
|
||||
}
|
||||
void run(int n) final {
|
||||
T checker_;
|
||||
for (int i = 0; i < n; i++) {
|
||||
checker_.check(i).ensure();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DuplicateCheckerBenchRepeat final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "DuplicateCheckerBenchRepeat" << T::get_description();
|
||||
}
|
||||
void run(int n) final {
|
||||
T checker_;
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto iter = i >> 10;
|
||||
auto pos = i - (iter << 10);
|
||||
if (pos < 768) {
|
||||
if (iter >= 3 && pos == 0) {
|
||||
auto error = checker_.check((iter - 3) * 768 + pos);
|
||||
CHECK(error.error().code() == 2);
|
||||
}
|
||||
checker_.check(iter * 768 + pos).ensure();
|
||||
} else {
|
||||
checker_.check(iter * 768 + pos - 256).ensure_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DuplicateCheckerBenchRepeatOnly final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "DuplicateCheckerBenchRepeatOnly" << T::get_description();
|
||||
}
|
||||
void run(int n) final {
|
||||
T checker_;
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto result = checker_.check(i & 255);
|
||||
CHECK(result.is_error() == (i >= 256));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DuplicateCheckerBenchReverse final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "DuplicateCheckerBenchReverseAdd" << T::get_description();
|
||||
}
|
||||
void run(int n) final {
|
||||
T checker_;
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto pos = i & 255;
|
||||
checker_.check(i - pos + (255 - pos)).ensure();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DuplicateCheckerBenchEvenOdd final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "DuplicateCheckerBenchEvenOdd" << T::get_description();
|
||||
}
|
||||
void run(int n) final {
|
||||
T checker_;
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto pos = i & 255;
|
||||
checker_.check(i - pos + (pos * 2) % 256 + (pos * 2) / 256).ensure();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BENCH(AddToTopStd, "add_to_top std") {
|
||||
td::vector<int> v;
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (size_t j = 0; j < 10; j++) {
|
||||
auto value = td::Random::fast(0, 9);
|
||||
auto it = std::find(v.begin(), v.end(), value);
|
||||
if (it == v.end()) {
|
||||
if (v.size() == 8) {
|
||||
v.back() = value;
|
||||
} else {
|
||||
v.push_back(value);
|
||||
}
|
||||
it = v.end() - 1;
|
||||
}
|
||||
std::rotate(v.begin(), it, it + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(AddToTopTd, "add_to_top td") {
|
||||
td::vector<int> v;
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (size_t j = 0; j < 10; j++) {
|
||||
td::add_to_top(v, 8, td::Random::fast(0, 9));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(AnyOfStd, "any_of std") {
|
||||
td::vector<int> v;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
v.push_back(i);
|
||||
}
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int rem = td::Random::fast(0, 127);
|
||||
res += static_cast<int>(std::any_of(v.begin(), v.end(), [rem](int x) { return (x & 127) == rem; }));
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(AnyOfTd, "any_of td") {
|
||||
td::vector<int> v;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
v.push_back(i);
|
||||
}
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int rem = td::Random::fast(0, 127);
|
||||
res += static_cast<int>(td::any_of(v, [rem](int x) { return (x & 127) == rem; }));
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
|
||||
|
||||
td::bench(AnyOfStdBench());
|
||||
td::bench(AnyOfTdBench());
|
||||
|
||||
td::bench(ToStringIntSmallBench());
|
||||
td::bench(ToStringIntBigBench());
|
||||
|
||||
td::bench(AddToTopStdBench());
|
||||
td::bench(AddToTopTdBench());
|
||||
|
||||
td::bench(TlToStringUpdateFileBench());
|
||||
td::bench(TlToStringMessageBench());
|
||||
|
||||
td::bench(DuplicateCheckerBenchEvenOdd<IdDuplicateCheckerNew<1000>>());
|
||||
td::bench(DuplicateCheckerBenchEvenOdd<IdDuplicateCheckerNew<300>>());
|
||||
td::bench(DuplicateCheckerBenchEvenOdd<IdDuplicateCheckerArray<1000>>());
|
||||
td::bench(DuplicateCheckerBenchEvenOdd<IdDuplicateCheckerArray<300>>());
|
||||
|
||||
td::bench(DuplicateCheckerBenchReverse<IdDuplicateCheckerNew<1000>>());
|
||||
td::bench(DuplicateCheckerBenchReverse<IdDuplicateCheckerNew<300>>());
|
||||
td::bench(DuplicateCheckerBenchReverse<IdDuplicateCheckerArray<1000>>());
|
||||
td::bench(DuplicateCheckerBenchReverse<IdDuplicateCheckerArray<300>>());
|
||||
|
||||
td::bench(DuplicateCheckerBenchRepeatOnly<IdDuplicateCheckerNew<1000>>());
|
||||
td::bench(DuplicateCheckerBenchRepeatOnly<IdDuplicateCheckerNew<300>>());
|
||||
td::bench(DuplicateCheckerBenchRepeatOnly<IdDuplicateCheckerArray<1000>>());
|
||||
td::bench(DuplicateCheckerBenchRepeatOnly<IdDuplicateCheckerArray<300>>());
|
||||
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerOld>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerNew<1000>>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerNewOther>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerNewSimple>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerNew<300>>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerArray<1000>>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerArray<300>>());
|
||||
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerOld>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNew<1000>>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNewOther>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNewSimple>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNew<300>>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNew<100>>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNew<10>>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerArray<1000>>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerArray<300>>());
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
for (int i = 1; i <= 16; i *= 2) {
|
||||
td::bench(ThreadSafeCounterBench(i));
|
||||
td::bench(AtomicCounterBench<false>(i));
|
||||
td::bench(AtomicCounterBench<true>(i));
|
||||
}
|
||||
|
||||
td::bench(AtomicReleaseIncBench<1>());
|
||||
td::bench(AtomicReleaseIncBench<2>());
|
||||
td::bench(AtomicReleaseCasIncBench<1>());
|
||||
td::bench(AtomicReleaseCasIncBench<2>());
|
||||
td::bench(RwMutexWriteBench<1>());
|
||||
td::bench(RwMutexReadBench<1>());
|
||||
td::bench(RwMutexWriteBench<2>());
|
||||
td::bench(RwMutexReadBench<2>());
|
||||
#endif
|
||||
#if !TD_WINDOWS
|
||||
td::bench(UtimeBench());
|
||||
#endif
|
||||
td::bench(WalkPathBench());
|
||||
td::bench(CreateFileBench());
|
||||
td::bench(PwriteBench());
|
||||
|
||||
td::bench(TlCallBench());
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
td::bench(ThreadNewBench());
|
||||
#endif
|
||||
#if !TD_EVENTFD_UNSUPPORTED
|
||||
td::bench(EventFdBench());
|
||||
#endif
|
||||
td::bench(NewObjBench());
|
||||
td::bench(NewIntBench());
|
||||
#if !TD_WINDOWS
|
||||
td::bench(PipeBench());
|
||||
#endif
|
||||
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
||||
td::bench(SemBench());
|
||||
#endif
|
||||
}
|
||||
932
td/benchmark/bench_queue.cpp
Normal file
932
td/benchmark/bench_queue.cpp
Normal file
@@ -0,0 +1,932 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/MpscPollableQueue.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/queue.h"
|
||||
#include "td/utils/Random.h"
|
||||
|
||||
// TODO: check system calls
|
||||
// TODO: all return values must be checked
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <semaphore.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if TD_LINUX
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
|
||||
#define MODE std::memory_order_relaxed
|
||||
|
||||
// void set_affinity(int mask) {
|
||||
// pid_t pid = gettid();
|
||||
// int syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(mask), &mask);
|
||||
// if (syscallres) {
|
||||
// perror("Failed to set affinity");
|
||||
// }
|
||||
// }
|
||||
|
||||
using qvalue_t = int;
|
||||
|
||||
class Backoff {
|
||||
int cnt = 0;
|
||||
|
||||
public:
|
||||
bool next() {
|
||||
cnt++;
|
||||
if (cnt < 50) {
|
||||
return true;
|
||||
} else {
|
||||
td::usleep_for(1);
|
||||
return cnt < 500;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
// Just for testing, not production
|
||||
class PipeQueue {
|
||||
int input;
|
||||
int output;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
int new_pipe[2];
|
||||
int res = pipe(new_pipe);
|
||||
CHECK(res == 0);
|
||||
output = new_pipe[0];
|
||||
input = new_pipe[1];
|
||||
}
|
||||
|
||||
void put(qvalue_t value) {
|
||||
auto len = write(input, &value, sizeof(value));
|
||||
CHECK(len == sizeof(value));
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
qvalue_t res;
|
||||
auto len = read(output, &res, sizeof(res));
|
||||
CHECK(len == sizeof(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
close(input);
|
||||
close(output);
|
||||
}
|
||||
};
|
||||
|
||||
class VarQueue {
|
||||
std::atomic<qvalue_t> data{0};
|
||||
|
||||
public:
|
||||
void init() {
|
||||
data.store(-1, MODE);
|
||||
}
|
||||
|
||||
void put(qvalue_t value) {
|
||||
data.store(value, MODE);
|
||||
}
|
||||
|
||||
qvalue_t try_get() {
|
||||
__sync_synchronize(); // TODO: it is wrong place for barrier, but it results in fastest queue
|
||||
qvalue_t res = data.load(MODE);
|
||||
return res;
|
||||
}
|
||||
|
||||
void acquire() {
|
||||
data.store(-1, MODE);
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
qvalue_t res;
|
||||
Backoff backoff;
|
||||
|
||||
do {
|
||||
res = try_get();
|
||||
} while (res == -1 && (backoff.next(), true));
|
||||
acquire();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
}
|
||||
};
|
||||
|
||||
class SemQueue {
|
||||
sem_t sem;
|
||||
VarQueue q;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
sem_init(&sem, 0, 0);
|
||||
}
|
||||
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
sem_post(&sem);
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
sem_wait(&sem);
|
||||
qvalue_t res = q.get();
|
||||
return res;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
sem_destroy(&sem);
|
||||
}
|
||||
|
||||
// HACK for benchmark
|
||||
void reader_flush() {
|
||||
}
|
||||
|
||||
void writer_flush() {
|
||||
}
|
||||
|
||||
void writer_put(qvalue_t value) {
|
||||
put(value);
|
||||
}
|
||||
|
||||
int reader_wait() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
qvalue_t reader_get_unsafe() {
|
||||
return get();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if TD_LINUX
|
||||
class EventfdQueue {
|
||||
int fd;
|
||||
VarQueue q;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
fd = eventfd(0, 0);
|
||||
}
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
td::int64 x = 1;
|
||||
auto len = write(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
}
|
||||
qvalue_t get() {
|
||||
td::int64 x;
|
||||
auto len = read(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
CHECK(x == 1);
|
||||
return q.get();
|
||||
}
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
close(fd);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
const int queue_buf_size = 1 << 10;
|
||||
|
||||
class BufferQueue {
|
||||
struct node {
|
||||
qvalue_t val;
|
||||
char pad[64 - sizeof(std::atomic<qvalue_t>)];
|
||||
};
|
||||
node q[queue_buf_size];
|
||||
|
||||
struct Position {
|
||||
std::atomic<td::uint32> i{0};
|
||||
char pad[64 - sizeof(std::atomic<td::uint32>)];
|
||||
|
||||
td::uint32 local_read_i;
|
||||
td::uint32 local_write_i;
|
||||
char pad2[64 - sizeof(td::uint32) * 2];
|
||||
|
||||
void init() {
|
||||
i = 0;
|
||||
local_read_i = 0;
|
||||
local_write_i = 0;
|
||||
}
|
||||
};
|
||||
|
||||
Position writer;
|
||||
Position reader;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
writer.init();
|
||||
reader.init();
|
||||
}
|
||||
|
||||
bool reader_empty() {
|
||||
return reader.local_write_i == reader.local_read_i;
|
||||
}
|
||||
|
||||
bool writer_empty() {
|
||||
return writer.local_write_i == writer.local_read_i + queue_buf_size;
|
||||
}
|
||||
|
||||
int reader_ready() {
|
||||
return static_cast<int>(reader.local_write_i - reader.local_read_i);
|
||||
}
|
||||
|
||||
int writer_ready() {
|
||||
return static_cast<int>(writer.local_read_i + queue_buf_size - writer.local_write_i);
|
||||
}
|
||||
|
||||
qvalue_t get_unsafe() {
|
||||
return q[reader.local_read_i++ & (queue_buf_size - 1)].val;
|
||||
}
|
||||
|
||||
void flush_reader() {
|
||||
reader.i.store(reader.local_read_i, std::memory_order_release);
|
||||
}
|
||||
|
||||
int update_reader() {
|
||||
reader.local_write_i = writer.i.load(std::memory_order_acquire);
|
||||
return reader_ready();
|
||||
}
|
||||
|
||||
void put_unsafe(qvalue_t val) {
|
||||
q[writer.local_write_i++ & (queue_buf_size - 1)].val = val;
|
||||
}
|
||||
|
||||
void flush_writer() {
|
||||
writer.i.store(writer.local_write_i, std::memory_order_release);
|
||||
}
|
||||
|
||||
int update_writer() {
|
||||
writer.local_read_i = reader.i.load(std::memory_order_acquire);
|
||||
return writer_ready();
|
||||
}
|
||||
|
||||
int wait_reader() {
|
||||
Backoff backoff;
|
||||
int res = 0;
|
||||
while (res == 0) {
|
||||
backoff.next();
|
||||
res = update_reader();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
qvalue_t get_noflush() {
|
||||
if (!reader_empty()) {
|
||||
return get_unsafe();
|
||||
}
|
||||
|
||||
Backoff backoff;
|
||||
while (true) {
|
||||
backoff.next();
|
||||
if (update_reader()) {
|
||||
return get_unsafe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
qvalue_t res = get_noflush();
|
||||
flush_reader();
|
||||
return res;
|
||||
}
|
||||
|
||||
void put_noflush(qvalue_t val) {
|
||||
if (!writer_empty()) {
|
||||
put_unsafe(val);
|
||||
return;
|
||||
}
|
||||
if (!update_writer()) {
|
||||
LOG(FATAL) << "Put strong failed";
|
||||
}
|
||||
put_unsafe(val);
|
||||
}
|
||||
|
||||
void put(qvalue_t val) {
|
||||
put_noflush(val);
|
||||
flush_writer();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
}
|
||||
};
|
||||
|
||||
#if TD_LINUX
|
||||
class BufferedFdQueue {
|
||||
int fd;
|
||||
std::atomic<int> wait_flag{0};
|
||||
BufferQueue q;
|
||||
char pad[64];
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
fd = eventfd(0, 0);
|
||||
(void)pad[0];
|
||||
}
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
td::int64 x = 1;
|
||||
__sync_synchronize();
|
||||
if (wait_flag.load(MODE)) {
|
||||
auto len = write(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
}
|
||||
}
|
||||
void put_noflush(qvalue_t value) {
|
||||
q.put_noflush(value);
|
||||
}
|
||||
void flush_writer() {
|
||||
q.flush_writer();
|
||||
td::int64 x = 1;
|
||||
__sync_synchronize();
|
||||
if (wait_flag.load(MODE)) {
|
||||
auto len = write(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
}
|
||||
}
|
||||
void flush_reader() {
|
||||
q.flush_reader();
|
||||
}
|
||||
|
||||
qvalue_t get_unsafe_flush() {
|
||||
qvalue_t res = q.get_unsafe();
|
||||
q.flush_reader();
|
||||
return res;
|
||||
}
|
||||
|
||||
qvalue_t get_unsafe() {
|
||||
return q.get_unsafe();
|
||||
}
|
||||
|
||||
int wait_reader() {
|
||||
int res = 0;
|
||||
Backoff backoff;
|
||||
while (res == 0 && backoff.next()) {
|
||||
res = q.update_reader();
|
||||
}
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
td::int64 x;
|
||||
wait_flag.store(1, MODE);
|
||||
__sync_synchronize();
|
||||
while (!(res = q.update_reader())) {
|
||||
auto len = read(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
__sync_synchronize();
|
||||
}
|
||||
wait_flag.store(0, MODE);
|
||||
return res;
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
if (!q.reader_empty()) {
|
||||
return get_unsafe_flush();
|
||||
}
|
||||
|
||||
Backoff backoff;
|
||||
while (backoff.next()) {
|
||||
if (q.update_reader()) {
|
||||
return get_unsafe_flush();
|
||||
}
|
||||
}
|
||||
|
||||
td::int64 x;
|
||||
wait_flag.store(1, MODE);
|
||||
__sync_synchronize();
|
||||
while (!q.update_reader()) {
|
||||
auto len = read(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
__sync_synchronize();
|
||||
}
|
||||
wait_flag.store(0, MODE);
|
||||
return get_unsafe_flush();
|
||||
}
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
close(fd);
|
||||
}
|
||||
};
|
||||
|
||||
class FdQueue {
|
||||
int fd;
|
||||
std::atomic<int> wait_flag{0};
|
||||
VarQueue q;
|
||||
char pad[64];
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
fd = eventfd(0, 0);
|
||||
(void)pad[0];
|
||||
}
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
td::int64 x = 1;
|
||||
__sync_synchronize();
|
||||
if (wait_flag.load(MODE)) {
|
||||
auto len = write(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
}
|
||||
}
|
||||
qvalue_t get() {
|
||||
// td::int64 x;
|
||||
// auto len = read(fd, &x, sizeof(x));
|
||||
// CHECK(len == sizeof(x));
|
||||
// return q.get();
|
||||
|
||||
Backoff backoff;
|
||||
qvalue_t res = -1;
|
||||
do {
|
||||
res = q.try_get();
|
||||
} while (res == -1 && backoff.next());
|
||||
if (res != -1) {
|
||||
q.acquire();
|
||||
return res;
|
||||
}
|
||||
|
||||
td::int64 x;
|
||||
wait_flag.store(1, MODE);
|
||||
__sync_synchronize();
|
||||
// while (res == -1 && read(fd, &x, sizeof(x)) == sizeof(x)) {
|
||||
// res = q.try_get();
|
||||
// }
|
||||
do {
|
||||
__sync_synchronize();
|
||||
res = q.try_get();
|
||||
} while (res == -1 && read(fd, &x, sizeof(x)) == sizeof(x));
|
||||
q.acquire();
|
||||
wait_flag.store(0, MODE);
|
||||
return res;
|
||||
}
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
close(fd);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
class SemBackoffQueue {
|
||||
sem_t sem;
|
||||
VarQueue q;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
sem_init(&sem, 0, 0);
|
||||
}
|
||||
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
sem_post(&sem);
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
Backoff backoff;
|
||||
int sem_flag = -1;
|
||||
do {
|
||||
sem_flag = sem_trywait(&sem);
|
||||
} while (sem_flag != 0 && backoff.next());
|
||||
if (sem_flag != 0) {
|
||||
sem_wait(&sem);
|
||||
}
|
||||
return q.get();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
sem_destroy(&sem);
|
||||
}
|
||||
};
|
||||
|
||||
class SemCheatQueue {
|
||||
sem_t sem;
|
||||
VarQueue q;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
sem_init(&sem, 0, 0);
|
||||
}
|
||||
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
sem_post(&sem);
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
Backoff backoff;
|
||||
qvalue_t res = -1;
|
||||
do {
|
||||
res = q.try_get();
|
||||
} while (res == -1 && backoff.next());
|
||||
sem_wait(&sem);
|
||||
if (res != -1) {
|
||||
q.acquire();
|
||||
return res;
|
||||
}
|
||||
return q.get();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
sem_destroy(&sem);
|
||||
}
|
||||
};
|
||||
|
||||
template <class QueueT>
|
||||
class QueueBenchmark2 final : public td::Benchmark {
|
||||
QueueT client, server;
|
||||
int connections_n, queries_n;
|
||||
|
||||
int server_active_connections;
|
||||
int client_active_connections;
|
||||
td::vector<td::int64> server_conn;
|
||||
td::vector<td::int64> client_conn;
|
||||
|
||||
td::string name;
|
||||
|
||||
public:
|
||||
QueueBenchmark2(int connections_n, td::string name) : connections_n(connections_n), name(std::move(name)) {
|
||||
}
|
||||
|
||||
td::string get_description() const final {
|
||||
return name;
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
client.init();
|
||||
server.init();
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
client.destroy();
|
||||
server.destroy();
|
||||
}
|
||||
|
||||
void server_process(qvalue_t value) {
|
||||
int no = value & 0x00FFFFFF;
|
||||
auto co = static_cast<int>(static_cast<td::uint32>(value) >> 24);
|
||||
CHECK(co >= 0 && co < connections_n);
|
||||
CHECK(no == server_conn[co]++);
|
||||
|
||||
client.writer_put(value);
|
||||
client.writer_flush();
|
||||
if (no + 1 >= queries_n) {
|
||||
server_active_connections--;
|
||||
}
|
||||
}
|
||||
|
||||
void *server_run(void *) {
|
||||
server_conn = td::vector<td::int64>(connections_n);
|
||||
server_active_connections = connections_n;
|
||||
|
||||
while (server_active_connections > 0) {
|
||||
int cnt = server.reader_wait();
|
||||
CHECK(cnt != 0);
|
||||
while (cnt-- > 0) {
|
||||
server_process(server.reader_get_unsafe());
|
||||
server.reader_flush();
|
||||
}
|
||||
// client.writer_flush();
|
||||
server.reader_flush();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void client_process(qvalue_t value) {
|
||||
int no = value & 0x00FFFFFF;
|
||||
auto co = static_cast<int>(static_cast<td::uint32>(value) >> 24);
|
||||
CHECK(co >= 0 && co < connections_n);
|
||||
CHECK(no == client_conn[co]++);
|
||||
if (no + 1 < queries_n) {
|
||||
server.writer_put(value + 1);
|
||||
server.writer_flush();
|
||||
} else {
|
||||
client_active_connections--;
|
||||
}
|
||||
}
|
||||
|
||||
void *client_run(void *) {
|
||||
client_conn = td::vector<td::int64>(connections_n);
|
||||
client_active_connections = connections_n;
|
||||
CHECK(queries_n < (1 << 24));
|
||||
|
||||
for (int i = 0; i < connections_n; i++) {
|
||||
server.writer_put(static_cast<qvalue_t>(i) << 24);
|
||||
}
|
||||
server.writer_flush();
|
||||
|
||||
while (client_active_connections > 0) {
|
||||
int cnt = client.reader_wait();
|
||||
CHECK(cnt != 0);
|
||||
while (cnt-- > 0) {
|
||||
client_process(client.reader_get_unsafe());
|
||||
client.reader_flush();
|
||||
}
|
||||
// server.writer_flush();
|
||||
client.reader_flush();
|
||||
}
|
||||
// system("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void *client_run_gateway(void *arg) {
|
||||
return static_cast<QueueBenchmark2 *>(arg)->client_run(nullptr);
|
||||
}
|
||||
|
||||
static void *server_run_gateway(void *arg) {
|
||||
return static_cast<QueueBenchmark2 *>(arg)->server_run(nullptr);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
pthread_t client_thread_id;
|
||||
pthread_t server_thread_id;
|
||||
|
||||
queries_n = (n + connections_n - 1) / connections_n;
|
||||
|
||||
pthread_create(&client_thread_id, nullptr, client_run_gateway, this);
|
||||
pthread_create(&server_thread_id, nullptr, server_run_gateway, this);
|
||||
|
||||
pthread_join(client_thread_id, nullptr);
|
||||
pthread_join(server_thread_id, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <class QueueT>
|
||||
class QueueBenchmark final : public td::Benchmark {
|
||||
QueueT client, server;
|
||||
const int connections_n;
|
||||
int queries_n;
|
||||
|
||||
td::string name;
|
||||
|
||||
public:
|
||||
QueueBenchmark(int connections_n, td::string name) : connections_n(connections_n), name(std::move(name)) {
|
||||
}
|
||||
|
||||
td::string get_description() const final {
|
||||
return name;
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
client.init();
|
||||
server.init();
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
client.destroy();
|
||||
server.destroy();
|
||||
}
|
||||
|
||||
void *server_run(void *) {
|
||||
td::vector<td::int64> conn(connections_n);
|
||||
int active_connections = connections_n;
|
||||
while (active_connections > 0) {
|
||||
qvalue_t value = server.get();
|
||||
int no = value & 0x00FFFFFF;
|
||||
auto co = static_cast<int>(value >> 24);
|
||||
CHECK(co >= 0 && co < connections_n);
|
||||
CHECK(no == conn[co]++);
|
||||
client.put(value);
|
||||
if (no + 1 >= queries_n) {
|
||||
active_connections--;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *client_run(void *) {
|
||||
td::vector<td::int64> conn(connections_n);
|
||||
CHECK(queries_n < (1 << 24));
|
||||
for (int i = 0; i < connections_n; i++) {
|
||||
server.put(static_cast<qvalue_t>(i) << 24);
|
||||
}
|
||||
int active_connections = connections_n;
|
||||
while (active_connections > 0) {
|
||||
qvalue_t value = client.get();
|
||||
int no = value & 0x00FFFFFF;
|
||||
auto co = static_cast<int>(value >> 24);
|
||||
CHECK(co >= 0 && co < connections_n);
|
||||
CHECK(no == conn[co]++);
|
||||
if (no + 1 < queries_n) {
|
||||
server.put(value + 1);
|
||||
} else {
|
||||
active_connections--;
|
||||
}
|
||||
}
|
||||
// system("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *client_run2(void *) {
|
||||
td::vector<td::int64> conn(connections_n);
|
||||
CHECK(queries_n < (1 << 24));
|
||||
for (int query = 0; query < queries_n; query++) {
|
||||
for (int i = 0; i < connections_n; i++) {
|
||||
server.put((static_cast<td::int64>(i) << 24) + query);
|
||||
}
|
||||
for (int i = 0; i < connections_n; i++) {
|
||||
qvalue_t value = client.get();
|
||||
int no = value & 0x00FFFFFF;
|
||||
auto co = static_cast<int>(value >> 24);
|
||||
CHECK(co >= 0 && co < connections_n);
|
||||
CHECK(no == conn[co]++);
|
||||
}
|
||||
}
|
||||
// system("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void *client_run_gateway(void *arg) {
|
||||
return static_cast<QueueBenchmark *>(arg)->client_run(nullptr);
|
||||
}
|
||||
|
||||
static void *server_run_gateway(void *arg) {
|
||||
return static_cast<QueueBenchmark *>(arg)->server_run(nullptr);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
pthread_t client_thread_id;
|
||||
pthread_t server_thread_id;
|
||||
|
||||
queries_n = (n + connections_n - 1) / connections_n;
|
||||
|
||||
pthread_create(&client_thread_id, nullptr, client_run_gateway, this);
|
||||
pthread_create(&server_thread_id, nullptr, server_run_gateway, this);
|
||||
|
||||
pthread_join(client_thread_id, nullptr);
|
||||
pthread_join(server_thread_id, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <class QueueT>
|
||||
class RingBenchmark final : public td::Benchmark {
|
||||
static constexpr int QN = 504;
|
||||
|
||||
struct Thread {
|
||||
int int_id;
|
||||
pthread_t id;
|
||||
QueueT queue;
|
||||
Thread *next;
|
||||
char pad[64];
|
||||
|
||||
void *run() {
|
||||
qvalue_t value;
|
||||
do {
|
||||
int cnt = queue.reader_wait();
|
||||
CHECK(cnt == 1);
|
||||
value = queue.reader_get_unsafe();
|
||||
queue.reader_flush();
|
||||
|
||||
next->queue.writer_put(value - 1);
|
||||
next->queue.writer_flush();
|
||||
} while (value >= QN);
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
Thread q[QN];
|
||||
|
||||
public:
|
||||
static void *run_gateway(void *arg) {
|
||||
return static_cast<Thread *>(arg)->run();
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
for (int i = 0; i < QN; i++) {
|
||||
q[i].int_id = i;
|
||||
q[i].queue.init();
|
||||
q[i].next = &q[(i + 1) % QN];
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
for (int i = 0; i < QN; i++) {
|
||||
q[i].queue.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < QN; i++) {
|
||||
pthread_create(&q[i].id, nullptr, run_gateway, &q[i]);
|
||||
}
|
||||
|
||||
if (n < 1000) {
|
||||
n = 1000;
|
||||
}
|
||||
q[0].queue.writer_put(n);
|
||||
q[0].queue.writer_flush();
|
||||
|
||||
for (int i = 0; i < QN; i++) {
|
||||
pthread_join(q[i].id, nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
||||
static void test_queue() {
|
||||
td::vector<td::thread> threads;
|
||||
static constexpr size_t THREAD_COUNT = 100;
|
||||
td::vector<td::MpscPollableQueue<int>> queues(THREAD_COUNT);
|
||||
for (auto &q : queues) {
|
||||
q.init();
|
||||
}
|
||||
for (size_t i = 0; i < THREAD_COUNT; i++) {
|
||||
threads.emplace_back([&q = queues[i]] {
|
||||
while (true) {
|
||||
auto ready_count = q.reader_wait_nonblock();
|
||||
while (ready_count-- > 0) {
|
||||
q.reader_get_unsafe();
|
||||
}
|
||||
q.reader_get_event_fd().wait(1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (size_t iter = 0; iter < THREAD_COUNT; iter++) {
|
||||
td::usleep_for(100);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
queues[td::Random::fast(0, THREAD_COUNT - 1)].writer_put(1);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < THREAD_COUNT; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
int main() {
|
||||
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
||||
// test_queue();
|
||||
#endif
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
// td::bench(RingBenchmark<SemQueue>());
|
||||
// td::bench(RingBenchmark<td::PollQueue<qvalue_t>>());
|
||||
|
||||
#define BENCH_Q2(Q, N) td::bench(QueueBenchmark2<Q<qvalue_t>>(N, #Q "(" #N ")"))
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
||||
BENCH_Q2(td::InfBackoffQueue, 1);
|
||||
BENCH_Q2(td::MpscPollableQueue, 1);
|
||||
BENCH_Q2(td::PollQueue, 1);
|
||||
|
||||
BENCH_Q2(td::InfBackoffQueue, 10);
|
||||
BENCH_Q2(td::MpscPollableQueue, 10);
|
||||
BENCH_Q2(td::PollQueue, 10);
|
||||
|
||||
BENCH_Q2(td::InfBackoffQueue, 100);
|
||||
BENCH_Q2(td::MpscPollableQueue, 100);
|
||||
BENCH_Q2(td::PollQueue, 100);
|
||||
|
||||
BENCH_Q2(td::PollQueue, 4);
|
||||
BENCH_Q2(td::PollQueue, 10);
|
||||
BENCH_Q2(td::PollQueue, 100);
|
||||
#endif
|
||||
|
||||
#define BENCH_Q(Q, N) td::bench(QueueBenchmark<Q>(N, #Q "(" #N ")"))
|
||||
|
||||
#if TD_LINUX
|
||||
BENCH_Q(BufferQueue, 1);
|
||||
BENCH_Q(BufferedFdQueue, 1);
|
||||
BENCH_Q(FdQueue, 1);
|
||||
#endif
|
||||
BENCH_Q(PipeQueue, 1);
|
||||
BENCH_Q(SemCheatQueue, 1);
|
||||
BENCH_Q(SemQueue, 1);
|
||||
BENCH_Q(VarQueue, 1);
|
||||
|
||||
#if TD_LINUX
|
||||
BENCH_Q(BufferQueue, 4);
|
||||
BENCH_Q(BufferQueue, 10);
|
||||
BENCH_Q(BufferQueue, 100);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
113
td/benchmark/bench_tddb.cpp
Normal file
113
td/benchmark/bench_tddb.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/telegram/DialogId.h"
|
||||
#include "td/telegram/MessageDb.h"
|
||||
#include "td/telegram/MessageId.h"
|
||||
#include "td/telegram/NotificationId.h"
|
||||
#include "td/telegram/ServerMessageId.h"
|
||||
#include "td/telegram/UserId.h"
|
||||
|
||||
#include "td/db/DbKey.h"
|
||||
#include "td/db/SqliteConnectionSafe.h"
|
||||
#include "td/db/SqliteDb.h"
|
||||
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Promise.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
static td::Status init_db(td::SqliteDb &db) {
|
||||
TRY_STATUS(db.exec("PRAGMA encoding=\"UTF-8\""));
|
||||
TRY_STATUS(db.exec("PRAGMA synchronous=NORMAL"));
|
||||
TRY_STATUS(db.exec("PRAGMA journal_mode=WAL"));
|
||||
TRY_STATUS(db.exec("PRAGMA temp_store=MEMORY"));
|
||||
TRY_STATUS(db.exec("PRAGMA secure_delete=1"));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
class MessageDbBench final : public td::Benchmark {
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
return "MessageDb";
|
||||
}
|
||||
void start_up() final {
|
||||
LOG(ERROR) << "START UP";
|
||||
do_start_up().ensure();
|
||||
scheduler_->start();
|
||||
}
|
||||
void run(int n) final {
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
for (int i = 0; i < n; i += 20) {
|
||||
auto dialog_id = td::DialogId(td::UserId(static_cast<td::int64>(td::Random::fast(1, 100))));
|
||||
auto message_id_raw = td::Random::fast(1, 100000);
|
||||
for (int j = 0; j < 20; j++) {
|
||||
auto message_id = td::MessageId{td::ServerMessageId{message_id_raw + j}};
|
||||
auto unique_message_id = td::ServerMessageId{i + 1};
|
||||
auto sender_dialog_id = td::DialogId(td::UserId(static_cast<td::int64>(td::Random::fast(1, 1000))));
|
||||
auto random_id = i + 1;
|
||||
auto ttl_expires_at = 0;
|
||||
auto data = td::BufferSlice(td::Random::fast(100, 299));
|
||||
|
||||
// use async on same thread.
|
||||
message_db_async_->add_message({dialog_id, message_id}, unique_message_id, sender_dialog_id, random_id,
|
||||
ttl_expires_at, 0, 0, "", td::NotificationId(), td::MessageId(), std::move(data),
|
||||
td::Promise<>());
|
||||
}
|
||||
}
|
||||
}
|
||||
void tear_down() final {
|
||||
scheduler_->run_main(0.1);
|
||||
{
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
sql_connection_.reset();
|
||||
message_db_sync_safe_.reset();
|
||||
message_db_async_.reset();
|
||||
}
|
||||
|
||||
scheduler_->finish();
|
||||
scheduler_.reset();
|
||||
LOG(ERROR) << "TEAR DOWN";
|
||||
}
|
||||
|
||||
private:
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
std::shared_ptr<td::SqliteConnectionSafe> sql_connection_;
|
||||
std::shared_ptr<td::MessageDbSyncSafeInterface> message_db_sync_safe_;
|
||||
std::shared_ptr<td::MessageDbAsyncInterface> message_db_async_;
|
||||
|
||||
td::Status do_start_up() {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(1, 0);
|
||||
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
|
||||
td::string sql_db_name = "testdb.sqlite";
|
||||
sql_connection_ = std::make_shared<td::SqliteConnectionSafe>(sql_db_name, td::DbKey::empty());
|
||||
auto &db = sql_connection_->get();
|
||||
TRY_STATUS(init_db(db));
|
||||
|
||||
db.exec("BEGIN TRANSACTION").ensure();
|
||||
// version == 0 ==> db will be destroyed
|
||||
TRY_STATUS(init_message_db(db, 0));
|
||||
db.exec("COMMIT TRANSACTION").ensure();
|
||||
|
||||
message_db_sync_safe_ = td::create_message_db_sync(sql_connection_);
|
||||
message_db_async_ = td::create_message_db_async(message_db_sync_safe_, 0);
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
|
||||
td::bench(MessageDbBench());
|
||||
}
|
||||
193
td/benchmark/check_proxy.cpp
Normal file
193
td/benchmark/check_proxy.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/telegram/Client.h"
|
||||
#include "td/telegram/td_api.h"
|
||||
|
||||
#include "td/utils/base64.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/TsCerr.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
static void usage() {
|
||||
td::TsCerr() << "Tests specified MTProto-proxies, outputs working proxies to stdout; exits with code 0 if a working "
|
||||
"proxy was found.\n";
|
||||
td::TsCerr() << "Usage: check_proxy [options] server:port:secret [server2:port2:secret2 ...]\n";
|
||||
td::TsCerr() << "Options:\n";
|
||||
td::TsCerr() << " -v<N>\tSet verbosity level to N\n";
|
||||
td::TsCerr() << " -h/--help\tDisplay this information\n";
|
||||
td::TsCerr() << " -d/--dc-id\tIdentifier of a datacenter to which try to connect (default is 2)\n";
|
||||
td::TsCerr() << " -l/--proxy-list\tName of a file with proxies to check; one proxy per line\n";
|
||||
td::TsCerr() << " -t/--timeout\tMaximum overall timeout for the request (default is 10 seconds)\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int new_verbosity_level = VERBOSITY_NAME(FATAL);
|
||||
|
||||
td::vector<std::pair<td::string, td::td_api::object_ptr<td::td_api::testProxy>>> requests;
|
||||
|
||||
auto add_proxy = [&requests](td::string arg) {
|
||||
if (arg.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t offset = 0;
|
||||
if (arg[0] == '[') {
|
||||
auto end_ipv6_pos = arg.find(']');
|
||||
if (end_ipv6_pos == td::string::npos) {
|
||||
td::TsCerr() << "Error: failed to find end of IPv6 address in \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
offset = end_ipv6_pos;
|
||||
}
|
||||
if (std::count(arg.begin() + offset, arg.end(), ':') == 3) {
|
||||
auto secret_domain_pos = arg.find(':', arg.find(':', offset) + 1) + 1;
|
||||
auto domain_pos = arg.find(':', secret_domain_pos);
|
||||
auto secret = arg.substr(secret_domain_pos, domain_pos - secret_domain_pos);
|
||||
auto domain = arg.substr(domain_pos + 1);
|
||||
auto r_decoded_secret = td::hex_decode(secret);
|
||||
if (r_decoded_secret.is_error()) {
|
||||
r_decoded_secret = td::base64url_decode(secret);
|
||||
if (r_decoded_secret.is_error()) {
|
||||
td::TsCerr() << "Error: failed to find proxy port and secret in \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
}
|
||||
arg = arg.substr(0, secret_domain_pos) + td::base64url_encode(r_decoded_secret.ok() + domain);
|
||||
}
|
||||
|
||||
auto secret_pos = arg.rfind(':');
|
||||
if (secret_pos == td::string::npos) {
|
||||
td::TsCerr() << "Error: failed to find proxy port and secret in \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
auto secret = arg.substr(secret_pos + 1);
|
||||
auto port_pos = arg.substr(0, secret_pos).rfind(':');
|
||||
if (port_pos == td::string::npos) {
|
||||
td::TsCerr() << "Error: failed to find proxy secret in \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
auto r_port = td::to_integer_safe<td::int32>(arg.substr(port_pos + 1, secret_pos - port_pos - 1));
|
||||
if (r_port.is_error()) {
|
||||
td::TsCerr() << "Error: failed to parse proxy port in \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
auto port = r_port.move_as_ok();
|
||||
auto server = arg.substr(0, port_pos);
|
||||
if (server[0] == '[' && server.back() == ']') {
|
||||
server = server.substr(1, server.size() - 2);
|
||||
}
|
||||
|
||||
if (server.empty() || port <= 0 || port > 65536 || secret.empty()) {
|
||||
td::TsCerr() << "Error: proxy address to check is in wrong format: \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
requests.emplace_back(arg,
|
||||
td::td_api::make_object<td::td_api::testProxy>(
|
||||
server, port, td::td_api::make_object<td::td_api::proxyTypeMtproto>(secret), -1, -1));
|
||||
};
|
||||
|
||||
td::int32 dc_id = 2;
|
||||
double timeout = 10.0;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
td::string arg(argv[i]);
|
||||
|
||||
auto get_next_arg = [&i, &arg, argc, argv](bool is_optional = false) {
|
||||
CHECK(arg.size() >= 2);
|
||||
if (arg.size() == 2 || arg[1] == '-') {
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
return td::string(argv[++i]);
|
||||
}
|
||||
} else {
|
||||
if (arg.size() > 2) {
|
||||
return arg.substr(2);
|
||||
}
|
||||
}
|
||||
if (!is_optional) {
|
||||
td::TsCerr() << "Error: value is required after " << arg << "\n";
|
||||
usage();
|
||||
}
|
||||
return td::string();
|
||||
};
|
||||
|
||||
if (td::begins_with(arg, "-v")) {
|
||||
arg = get_next_arg(true);
|
||||
int new_verbosity = 1;
|
||||
while (arg[0] == 'v') {
|
||||
new_verbosity++;
|
||||
arg = arg.substr(1);
|
||||
}
|
||||
if (!arg.empty()) {
|
||||
new_verbosity += td::to_integer<int>(arg) - (new_verbosity == 1);
|
||||
}
|
||||
new_verbosity_level = VERBOSITY_NAME(FATAL) + new_verbosity;
|
||||
} else if (td::begins_with(arg, "-t") || arg == "--timeout") {
|
||||
timeout = td::to_double(get_next_arg());
|
||||
} else if (td::begins_with(arg, "-d") || arg == "--dc-id") {
|
||||
dc_id = td::to_integer<td::int32>(get_next_arg());
|
||||
} else if (td::begins_with(arg, "-l") || arg == "--proxy-list") {
|
||||
auto r_proxies = td::read_file_str(get_next_arg());
|
||||
if (r_proxies.is_error()) {
|
||||
td::TsCerr() << "Error: wrong file name specified\n";
|
||||
usage();
|
||||
}
|
||||
for (auto &proxy : td::full_split(r_proxies.ok(), '\n')) {
|
||||
add_proxy(td::trim(proxy));
|
||||
}
|
||||
} else if (arg[0] == '-') {
|
||||
usage();
|
||||
} else {
|
||||
add_proxy(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (requests.empty()) {
|
||||
td::TsCerr() << "Error: proxy address to check is not specified\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
SET_VERBOSITY_LEVEL(new_verbosity_level);
|
||||
|
||||
td::ClientManager client_manager;
|
||||
auto client_id = client_manager.create_client_id();
|
||||
for (size_t i = 0; i < requests.size(); i++) {
|
||||
auto &request = requests[i].second;
|
||||
request->dc_id_ = dc_id;
|
||||
request->timeout_ = timeout;
|
||||
client_manager.send(client_id, i + 1, std::move(request));
|
||||
}
|
||||
size_t successful_requests = 0;
|
||||
size_t failed_requests = 0;
|
||||
|
||||
while (successful_requests + failed_requests != requests.size()) {
|
||||
auto response = client_manager.receive(100.0);
|
||||
CHECK(client_id == response.client_id);
|
||||
if (1 <= response.request_id && response.request_id <= requests.size()) {
|
||||
auto &proxy = requests[static_cast<size_t>(response.request_id - 1)].first;
|
||||
if (response.object->get_id() == td::td_api::error::ID) {
|
||||
LOG(ERROR) << proxy << ": " << to_string(response.object);
|
||||
failed_requests++;
|
||||
} else {
|
||||
std::cout << proxy << std::endl;
|
||||
successful_requests++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (successful_requests == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
336
td/benchmark/check_tls.cpp
Normal file
336
td/benchmark/check_tls.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/utils/BigNum.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/detail/Iocp.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
static td::BigNumContext context;
|
||||
|
||||
static bool is_quadratic_residue(const td::BigNum &a) {
|
||||
// 2^255 - 19
|
||||
td::BigNum mod =
|
||||
td::BigNum::from_hex("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed").move_as_ok();
|
||||
// (mod - 1) / 2 = 2^254 - 10
|
||||
td::BigNum pow =
|
||||
td::BigNum::from_hex("3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6").move_as_ok();
|
||||
|
||||
td::BigNum r;
|
||||
td::BigNum::mod_exp(r, a, pow, mod, context);
|
||||
td::BigNum one = td::BigNum::from_decimal("1").move_as_ok();
|
||||
td::BigNum::mod_add(r, r, one, mod, context);
|
||||
|
||||
td::string result = r.to_decimal();
|
||||
CHECK(result == "0" || result == "1" || result == "2");
|
||||
return result == "2";
|
||||
}
|
||||
|
||||
struct TlsInfo {
|
||||
td::vector<size_t> extension_list;
|
||||
td::vector<size_t> encrypted_application_data_length;
|
||||
};
|
||||
|
||||
td::Result<TlsInfo> test_tls(const td::string &url) {
|
||||
td::IPAddress address;
|
||||
TRY_STATUS(address.init_host_port(url, 443));
|
||||
TRY_RESULT(socket, td::SocketFd::open(address));
|
||||
|
||||
td::string request;
|
||||
|
||||
auto add_string = [&](td::Slice data) {
|
||||
request.append(data.data(), data.size());
|
||||
};
|
||||
auto add_random = [&](size_t length) {
|
||||
while (length-- > 0) {
|
||||
request += static_cast<char>(td::Random::secure_int32());
|
||||
}
|
||||
};
|
||||
auto add_length = [&](size_t length) {
|
||||
request += static_cast<char>(length / 256);
|
||||
request += static_cast<char>(length % 256);
|
||||
};
|
||||
auto add_key = [&] {
|
||||
td::string key(32, '\0');
|
||||
td::BigNum mod =
|
||||
td::BigNum::from_hex("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed").move_as_ok();
|
||||
while (true) {
|
||||
td::Random::secure_bytes(key);
|
||||
key[31] = static_cast<char>(key[31] & 127);
|
||||
td::BigNum x = td::BigNum::from_le_binary(key);
|
||||
if (!is_quadratic_residue(x)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
td::BigNum y = x.clone();
|
||||
td::BigNum coef = td::BigNum::from_decimal("486662").move_as_ok();
|
||||
td::BigNum::mod_add(y, y, coef, mod, context);
|
||||
td::BigNum::mod_mul(y, y, x, mod, context);
|
||||
td::BigNum one = td::BigNum::from_decimal("1").move_as_ok();
|
||||
td::BigNum::mod_add(y, y, one, mod, context);
|
||||
td::BigNum::mod_mul(y, y, x, mod, context);
|
||||
// y = x^3 + 486662 * x^2 + x
|
||||
if (is_quadratic_residue(y)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
request += key;
|
||||
};
|
||||
|
||||
const size_t MAX_GREASE = 7;
|
||||
char greases[MAX_GREASE];
|
||||
td::Random::secure_bytes(td::MutableSlice{greases, MAX_GREASE});
|
||||
for (auto &grease : greases) {
|
||||
grease = static_cast<char>((grease & 0xF0) + 0x0A);
|
||||
}
|
||||
for (size_t i = 1; i < MAX_GREASE; i += 2) {
|
||||
if (greases[i] == greases[i - 1]) {
|
||||
greases[i] = static_cast<char>(0x10 ^ greases[i]);
|
||||
}
|
||||
}
|
||||
auto add_grease = [&](size_t num) {
|
||||
auto c = greases[num];
|
||||
request += c;
|
||||
request += c;
|
||||
};
|
||||
|
||||
add_string("\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03");
|
||||
add_random(32);
|
||||
add_string("\x20");
|
||||
add_random(32);
|
||||
add_string("\x00\x20");
|
||||
add_grease(0);
|
||||
add_string(
|
||||
"\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00"
|
||||
"\x2f\x00\x35\x01\x00\x01\x93");
|
||||
add_grease(2);
|
||||
add_string("\x00\x00\x00\x00");
|
||||
add_length(url.size() + 5);
|
||||
add_length(url.size() + 3);
|
||||
add_string("\x00");
|
||||
add_length(url.size());
|
||||
add_string(url);
|
||||
add_string("\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\x0a\x00\x0a\x00\x08");
|
||||
add_grease(4);
|
||||
add_string(
|
||||
"\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08\x68"
|
||||
"\x74\x74\x70\x2f\x31\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x12\x00\x10\x04\x03\x08\x04\x04"
|
||||
"\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29");
|
||||
add_grease(4);
|
||||
add_string("\x00\x01\x00\x00\x1d\x00\x20");
|
||||
add_key();
|
||||
add_string("\x00\x2d\x00\x02\x01\x01\x00\x2b\x00\x0b\x0a");
|
||||
add_grease(6);
|
||||
add_string("\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02");
|
||||
add_grease(3);
|
||||
add_string("\x00\x01\x00\x00\x15");
|
||||
auto padding = 515 - static_cast<int>(request.size());
|
||||
CHECK(padding >= 0);
|
||||
add_length(padding);
|
||||
request.resize(517);
|
||||
|
||||
// LOG(ERROR) << td::format::as_hex_dump<0>(td::Slice(request));
|
||||
|
||||
TRY_STATUS(socket.write(request));
|
||||
|
||||
TlsInfo info;
|
||||
auto end_time = td::Time::now() + 3;
|
||||
td::string result;
|
||||
size_t pos = 0;
|
||||
size_t server_hello_length = 0;
|
||||
size_t encrypted_application_data_length_sum = 0;
|
||||
while (td::Time::now() < end_time) {
|
||||
static char buf[20000];
|
||||
TRY_RESULT(res, socket.read(td::MutableSlice{buf, sizeof(buf)}));
|
||||
if (res > 0) {
|
||||
auto read_length = [&]() -> size_t {
|
||||
CHECK(result.size() >= 2 + pos);
|
||||
pos += 2;
|
||||
return static_cast<unsigned char>(result[pos - 2]) * 256 + static_cast<unsigned char>(result[pos - 1]);
|
||||
};
|
||||
|
||||
result += td::Slice(buf, res).str();
|
||||
while (true) {
|
||||
#define CHECK_LENGTH(length) \
|
||||
if (pos + (length) > result.size()) { \
|
||||
break; \
|
||||
}
|
||||
#define EXPECT_STR(pos, str, error) \
|
||||
if (!begins_with(td::Slice(result).substr(pos), str)) { \
|
||||
return td::Status::Error(error); \
|
||||
}
|
||||
|
||||
if (pos == 0) {
|
||||
CHECK_LENGTH(3);
|
||||
EXPECT_STR(0, "\x16\x03\x03", "Non-TLS response or TLS <= 1.1");
|
||||
pos += 3;
|
||||
}
|
||||
if (pos == 3) {
|
||||
CHECK_LENGTH(2);
|
||||
server_hello_length = read_length();
|
||||
if (server_hello_length <= 39) {
|
||||
return td::Status::Error("Receive too short server hello");
|
||||
}
|
||||
}
|
||||
if (server_hello_length > 0) {
|
||||
if (pos == 5) {
|
||||
CHECK_LENGTH(server_hello_length);
|
||||
|
||||
EXPECT_STR(5, "\x02\x00", "Non-TLS response 2");
|
||||
EXPECT_STR(9, "\x03\x03", "Non-TLS response 3");
|
||||
|
||||
auto random_id = td::Slice(result.c_str() + 11, 32);
|
||||
if (random_id ==
|
||||
"\xcf\x21\xad\x74\xe5\x9a\x61\x11\xbe\x1d\x8c\x02\x1e\x65\xb8\x91\xc2\xa2\x11\x16\x7a\xbb\x8c\x5e\x07"
|
||||
"\x9e\x09\xe2\xc8\xa8\x33\x9c") {
|
||||
return td::Status::Error("TLS 1.3 servers returning HelloRetryRequest are not supprted");
|
||||
}
|
||||
if (result[43] == '\x00') {
|
||||
return td::Status::Error("TLS <= 1.2: empty session_id");
|
||||
}
|
||||
EXPECT_STR(43, "\x20", "Non-TLS response 4");
|
||||
if (server_hello_length <= 75) {
|
||||
return td::Status::Error("Receive too short server hello 2");
|
||||
}
|
||||
EXPECT_STR(44, request.substr(44, 32), "TLS <= 1.2: expected mirrored session_id");
|
||||
EXPECT_STR(76, "\x13\x01\x00", "TLS <= 1.2: expected 0x1301 as a chosen cipher");
|
||||
pos += 74;
|
||||
size_t extensions_length = read_length();
|
||||
if (extensions_length + 76 != server_hello_length) {
|
||||
return td::Status::Error("Receive wrong extensions length");
|
||||
}
|
||||
while (pos < 5 + server_hello_length - 4) {
|
||||
info.extension_list.push_back(read_length());
|
||||
size_t extension_length = read_length();
|
||||
if (pos + extension_length > 5 + server_hello_length) {
|
||||
return td::Status::Error("Receive wrong extension length");
|
||||
}
|
||||
pos += extension_length;
|
||||
}
|
||||
if (pos != 5 + server_hello_length) {
|
||||
return td::Status::Error("Receive wrong extensions list");
|
||||
}
|
||||
}
|
||||
if (pos == 5 + server_hello_length) {
|
||||
CHECK_LENGTH(6);
|
||||
EXPECT_STR(pos, "\x14\x03\x03\x00\x01\x01", "Expected dummy ChangeCipherSpec");
|
||||
pos += 6;
|
||||
}
|
||||
if (pos == 11 + server_hello_length + encrypted_application_data_length_sum) {
|
||||
if (pos == result.size()) {
|
||||
return info;
|
||||
}
|
||||
|
||||
CHECK_LENGTH(3);
|
||||
EXPECT_STR(pos, "\x17\x03\x03", "Expected encrypted application data");
|
||||
pos += 3;
|
||||
}
|
||||
if (pos == 14 + server_hello_length + encrypted_application_data_length_sum) {
|
||||
CHECK_LENGTH(2);
|
||||
size_t encrypted_application_data_length = read_length();
|
||||
info.encrypted_application_data_length.push_back(encrypted_application_data_length);
|
||||
if (encrypted_application_data_length == 0) {
|
||||
return td::Status::Error("Receive empty encrypted application data");
|
||||
}
|
||||
}
|
||||
if (pos == 16 + server_hello_length + encrypted_application_data_length_sum) {
|
||||
CHECK_LENGTH(info.encrypted_application_data_length.back());
|
||||
pos += info.encrypted_application_data_length.back();
|
||||
encrypted_application_data_length_sum += info.encrypted_application_data_length.back() + 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
td::usleep_for(10000);
|
||||
}
|
||||
}
|
||||
|
||||
// LOG(ERROR) << url << ":" << td::format::as_hex_dump<0>(td::Slice(result));
|
||||
return td::Status::Error("Failed to get response in 3 seconds");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
auto iocp = td::make_unique<td::detail::Iocp>();
|
||||
iocp->init();
|
||||
auto iocp_thread = td::thread([&iocp] { iocp->loop(); });
|
||||
td::detail::Iocp::Guard iocp_guard(iocp.get());
|
||||
#endif
|
||||
|
||||
td::vector<td::string> urls;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
urls.emplace_back(argv[i]);
|
||||
}
|
||||
for (auto &url : urls) {
|
||||
const int MAX_TRIES = 100;
|
||||
td::vector<std::map<size_t, int>> length_count;
|
||||
td::vector<size_t> extension_list;
|
||||
for (int i = 0; i < MAX_TRIES; i++) {
|
||||
auto r_tls_info = test_tls(url);
|
||||
if (r_tls_info.is_error()) {
|
||||
LOG(ERROR) << url << ": " << r_tls_info.error();
|
||||
break;
|
||||
} else {
|
||||
auto tls_info = r_tls_info.move_as_ok();
|
||||
if (length_count.size() < tls_info.encrypted_application_data_length.size()) {
|
||||
length_count.resize(tls_info.encrypted_application_data_length.size());
|
||||
}
|
||||
for (size_t t = 0; t < tls_info.encrypted_application_data_length.size(); t++) {
|
||||
length_count[t][tls_info.encrypted_application_data_length[t]]++;
|
||||
}
|
||||
if (i == 0) {
|
||||
extension_list = tls_info.extension_list;
|
||||
} else {
|
||||
if (extension_list != tls_info.extension_list) {
|
||||
LOG(ERROR) << url << ": TLS 1.3.0 extension list has changed from " << extension_list << " to "
|
||||
<< tls_info.extension_list;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAX_TRIES - 1) {
|
||||
if (extension_list != td::vector<size_t>{51, 43} && extension_list != td::vector<size_t>{43, 51}) {
|
||||
LOG(ERROR) << url << ": TLS 1.3.0 unsupported extension list " << extension_list;
|
||||
} else {
|
||||
td::string length_distribution = "|";
|
||||
for (size_t t = 0; t < length_count.size(); t++) {
|
||||
for (auto it : length_count[t]) {
|
||||
length_distribution += PSTRING()
|
||||
<< it.first << " : " << static_cast<int>(it.second * 100.0 / MAX_TRIES) << "%|";
|
||||
}
|
||||
if (t + 1 != length_count.size()) {
|
||||
length_distribution += " + |";
|
||||
}
|
||||
}
|
||||
LOG(ERROR) << url << ": TLS 1.3.0 with extensions " << extension_list << " and "
|
||||
<< (length_count.size() != 1 ? "unsupported " : "")
|
||||
<< "encrypted application data length distribution " << length_distribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
iocp->interrupt_loop();
|
||||
iocp_thread.join();
|
||||
#endif
|
||||
}
|
||||
545
td/benchmark/hashmap_build.cpp
Normal file
545
td/benchmark/hashmap_build.cpp
Normal file
@@ -0,0 +1,545 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/utils/FlatHashMap.h"
|
||||
|
||||
#ifdef SCOPE_EXIT
|
||||
#undef SCOPE_EXIT
|
||||
#endif
|
||||
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#include <array>
|
||||
#include <folly/container/F14Map.h>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#define test_map td::FlatHashMap
|
||||
//#define test_map folly::F14FastMap
|
||||
//#define test_map absl::flat_hash_map
|
||||
//#define test_map std::map
|
||||
//#define test_map std::unordered_map
|
||||
|
||||
//#define CREATE_MAP(num) CREATE_MAP_IMPL(num)
|
||||
#define CREATE_MAP(num)
|
||||
|
||||
#define CREATE_MAP_IMPL(num) \
|
||||
int f_##num() { \
|
||||
test_map<td::int32, std::array<char, num>> m; \
|
||||
m.emplace(1, std::array<char, num>{}); \
|
||||
int sum = 0; \
|
||||
for (auto &it : m) { \
|
||||
sum += it.first; \
|
||||
} \
|
||||
auto it = m.find(1); \
|
||||
sum += it->first; \
|
||||
m.erase(it); \
|
||||
return sum; \
|
||||
} \
|
||||
int x_##num = f_##num()
|
||||
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
|
||||
int main() {
|
||||
}
|
||||
193
td/benchmark/hashset_memory.cpp
Normal file
193
td/benchmark/hashset_memory.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#if USE_MEMPROF
|
||||
#include "memprof/memprof_stat.h"
|
||||
#endif
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/FlatHashMap.h"
|
||||
#include "td/utils/FlatHashMapChunks.h"
|
||||
#include "td/utils/FlatHashTable.h"
|
||||
#include "td/utils/HashTableUtils.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/MapNode.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/Stat.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#ifdef SCOPE_EXIT
|
||||
#undef SCOPE_EXIT
|
||||
#endif
|
||||
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#include <array>
|
||||
#include <folly/container/F14Map.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
static int mem_stat_i = -1;
|
||||
static int mem_stat_cur = 0;
|
||||
|
||||
static bool use_memprof() {
|
||||
#if USE_MEMPROF
|
||||
return mem_stat_i < 0 && is_memprof_on();
|
||||
#else
|
||||
return mem_stat_i < 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static td::uint64 get_memory() {
|
||||
#if USE_MEMPROF
|
||||
if (use_memprof()) {
|
||||
return get_used_memory_size();
|
||||
}
|
||||
#endif
|
||||
CHECK(!use_memprof());
|
||||
return td::mem_stat().ok().resident_size_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class Generator {
|
||||
public:
|
||||
T next() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
static size_t dyn_size() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class IntGenerator {
|
||||
public:
|
||||
T next() {
|
||||
return ++value;
|
||||
}
|
||||
static size_t dyn_size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
T value{};
|
||||
};
|
||||
|
||||
template <>
|
||||
class Generator<td::int32> final : public IntGenerator<td::int32> {};
|
||||
template <>
|
||||
class Generator<td::int64> final : public IntGenerator<td::int64> {};
|
||||
|
||||
template <class T>
|
||||
class Generator<td::unique_ptr<T>> {
|
||||
public:
|
||||
td::unique_ptr<T> next() {
|
||||
return td::make_unique<T>();
|
||||
}
|
||||
static std::size_t dyn_size() {
|
||||
return sizeof(T);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class KeyT, class ValueT>
|
||||
static void measure(td::StringBuilder &sb, td::Slice name, td::Slice key_name, td::Slice value_name) {
|
||||
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";
|
||||
std::size_t ideal_size = sizeof(KeyT) + sizeof(ValueT) + Generator<ValueT>::dyn_size();
|
||||
|
||||
sb << "\tempty:" << sizeof(T);
|
||||
struct Stat {
|
||||
int pi;
|
||||
double min_ratio;
|
||||
double max_ratio;
|
||||
};
|
||||
td::vector<Stat> stat;
|
||||
stat.reserve(1024);
|
||||
for (std::size_t size : {1000000u}) {
|
||||
Generator<KeyT> key_generator;
|
||||
Generator<ValueT> value_generator;
|
||||
auto start_mem = get_memory();
|
||||
T ht;
|
||||
auto ratio = [&] {
|
||||
auto end_mem = get_memory();
|
||||
auto used_mem = end_mem - start_mem;
|
||||
return static_cast<double>(used_mem) / (static_cast<double>(ideal_size) * static_cast<double>(ht.size()));
|
||||
};
|
||||
double min_ratio;
|
||||
double max_ratio;
|
||||
auto reset = [&] {
|
||||
min_ratio = 1e100;
|
||||
max_ratio = 0;
|
||||
};
|
||||
auto update = [&] {
|
||||
auto x = ratio();
|
||||
min_ratio = td::min(min_ratio, x);
|
||||
max_ratio = td::max(max_ratio, x);
|
||||
};
|
||||
reset();
|
||||
|
||||
int p = 10;
|
||||
int pi = 1;
|
||||
for (std::size_t i = 0; i < size; i++) {
|
||||
ht.emplace(key_generator.next(), value_generator.next());
|
||||
update();
|
||||
if ((i + 1) % p == 0) {
|
||||
stat.push_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';
|
||||
}
|
||||
|
||||
template <std::size_t size>
|
||||
using Bytes = std::array<char, size>;
|
||||
|
||||
template <template <typename... Args> class T>
|
||||
void print_memory_stats(td::Slice name) {
|
||||
td::string big_buff(1 << 16, '\0');
|
||||
td::StringBuilder sb(big_buff, false);
|
||||
#define MEASURE(KeyT, ValueT) measure<T<KeyT, ValueT>, KeyT, ValueT>(sb, name, #KeyT, #ValueT);
|
||||
MEASURE(td::int32, td::int32);
|
||||
MEASURE(td::int64, td::unique_ptr<Bytes<360>>);
|
||||
if (!sb.as_cslice().empty()) {
|
||||
LOG(PLAIN) << '\n' << sb.as_cslice() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
template <class KeyT, class ValueT, class HashT = td::Hash<KeyT>, class EqT = std::equal_to<KeyT>>
|
||||
using FlatHashMapImpl = td::FlatHashTable<td::MapNode<KeyT, ValueT>, HashT, EqT>;
|
||||
|
||||
#define FOR_EACH_TABLE(F) \
|
||||
F(FlatHashMapImpl) \
|
||||
F(folly::F14FastMap) \
|
||||
F(absl::flat_hash_map) \
|
||||
F(std::unordered_map) \
|
||||
F(std::map)
|
||||
#define BENCHMARK_MEMORY(T) print_memory_stats<T>(#T);
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Usage:
|
||||
// % benchmark/memory-hashset-os 0
|
||||
// Number of benchmarks = 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]));
|
||||
}
|
||||
FOR_EACH_TABLE(BENCHMARK_MEMORY);
|
||||
if (mem_stat_i <= 0) {
|
||||
LOG(PLAIN) << "Number of benchmarks = " << mem_stat_cur << "\n";
|
||||
}
|
||||
}
|
||||
45
td/benchmark/rmdir.cpp
Normal file
45
td/benchmark/rmdir.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 1) {
|
||||
return 1;
|
||||
}
|
||||
td::CSlice dir(argv[1]);
|
||||
int cnt = 0;
|
||||
auto status = td::walk_path(dir, [&](td::CSlice path, auto type) {
|
||||
if (type != td::WalkPath::Type::EnterDir) {
|
||||
cnt++;
|
||||
}
|
||||
auto type_name = [&] {
|
||||
switch (type) {
|
||||
case td::WalkPath::Type::EnterDir:
|
||||
return td::CSlice("Open");
|
||||
case td::WalkPath::Type::ExitDir:
|
||||
return td::CSlice("Exit");
|
||||
case td::WalkPath::Type::RegularFile:
|
||||
return td::CSlice("File");
|
||||
case td::WalkPath::Type::Symlink:
|
||||
return td::CSlice("Link");
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return td::CSlice();
|
||||
}
|
||||
}();
|
||||
LOG(INFO) << type_name << ' ' << path;
|
||||
//if (is_dir) {
|
||||
// td::rmdir(path);
|
||||
//} else {
|
||||
// td::unlink(path);
|
||||
//}
|
||||
});
|
||||
LOG(INFO) << status << ": " << cnt;
|
||||
}
|
||||
43
td/benchmark/wget.cpp
Normal file
43
td/benchmark/wget.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/Wget.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Promise.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
|
||||
td::VERBOSITY_NAME(fd) = VERBOSITY_NAME(INFO);
|
||||
|
||||
td::string url = (argc > 1 ? argv[1] : "https://telegram.org");
|
||||
auto timeout = 10;
|
||||
auto ttl = 3;
|
||||
auto prefer_ipv6 = (argc > 2 && td::string(argv[2]) == "-6");
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>(0, 0);
|
||||
scheduler
|
||||
->create_actor_unsafe<td::Wget>(0, "Client",
|
||||
td::PromiseCreator::lambda([](td::Result<td::unique_ptr<td::HttpQuery>> res) {
|
||||
if (res.is_error()) {
|
||||
LOG(FATAL) << res.error();
|
||||
}
|
||||
LOG(ERROR) << *res.ok();
|
||||
td::Scheduler::instance()->finish();
|
||||
}),
|
||||
url, td::Auto(), timeout, ttl, prefer_ipv6)
|
||||
.release();
|
||||
scheduler->start();
|
||||
while (scheduler->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler->finish();
|
||||
}
|
||||
Reference in New Issue
Block a user