This commit is contained in:
2024-10-10 19:05:48 +00:00
commit cffdcba6af
1880 changed files with 813614 additions and 0 deletions

View 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()

View 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));
}

View 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
View 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());
}

View 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() {
}

View 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());
}

View 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();
}

View 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());
}

View 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();
}

View 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();
}

View 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
View 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
View 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 &sum;
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
}

View 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
View 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());
}

View 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
View 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
}

View 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() {
}

View 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
View 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
View 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();
}