summaryrefslogtreecommitdiff
path: root/libs/tdlib/td/benchmark
diff options
context:
space:
mode:
Diffstat (limited to 'libs/tdlib/td/benchmark')
-rw-r--r--libs/tdlib/td/benchmark/CMakeLists.txt59
-rw-r--r--libs/tdlib/td/benchmark/bench_actor.cpp290
-rw-r--r--libs/tdlib/td/benchmark/bench_crypto.cpp213
-rw-r--r--libs/tdlib/td/benchmark/bench_db.cpp237
-rw-r--r--libs/tdlib/td/benchmark/bench_empty.cpp9
-rw-r--r--libs/tdlib/td/benchmark/bench_handshake.cpp76
-rw-r--r--libs/tdlib/td/benchmark/bench_http.cpp78
-rw-r--r--libs/tdlib/td/benchmark/bench_http_reader.cpp118
-rw-r--r--libs/tdlib/td/benchmark/bench_http_server.cpp89
-rw-r--r--libs/tdlib/td/benchmark/bench_http_server_cheat.cpp138
-rw-r--r--libs/tdlib/td/benchmark/bench_http_server_fast.cpp121
-rw-r--r--libs/tdlib/td/benchmark/bench_log.cpp163
-rw-r--r--libs/tdlib/td/benchmark/bench_misc.cpp392
-rw-r--r--libs/tdlib/td/benchmark/bench_queue.cpp943
-rw-r--r--libs/tdlib/td/benchmark/bench_tddb.cpp108
-rw-r--r--libs/tdlib/td/benchmark/rmdir.cpp28
-rw-r--r--libs/tdlib/td/benchmark/wget.cpp39
17 files changed, 3101 insertions, 0 deletions
diff --git a/libs/tdlib/td/benchmark/CMakeLists.txt b/libs/tdlib/td/benchmark/CMakeLists.txt
new file mode 100644
index 0000000000..90f294fbc9
--- /dev/null
+++ b/libs/tdlib/td/benchmark/CMakeLists.txt
@@ -0,0 +1,59 @@
+cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
+
+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 tdcore tdutils ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES})
+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 tdcore 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(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()
diff --git a/libs/tdlib/td/benchmark/bench_actor.cpp b/libs/tdlib/td/benchmark/bench_actor.cpp
new file mode 100644
index 0000000000..a966d601cf
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_actor.cpp
@@ -0,0 +1,290 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/actor/actor.h"
+#include "td/actor/PromiseFuture.h"
+
+#include "td/utils/logging.h"
+
+#include <algorithm>
+
+#if TD_MSVC
+#pragma comment(linker, "/STACK:16777216")
+#endif
+
+template <int type>
+class RingBench : public td::Benchmark {
+ public:
+ struct PassActor;
+
+ private:
+ int actor_n_;
+ int thread_n_;
+ std::vector<td::ActorId<PassActor>> actor_array_;
+ td::ConcurrentScheduler *scheduler_;
+
+ public:
+ std::string get_description() const override {
+ 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 : public td::Actor {
+ int id;
+ 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
+ // may be send event could be further optimized?
+ ::td::Scheduler::instance()->hack(static_cast<td::ActorId<Actor>>(next_actor),
+ td::Event::raw(static_cast<td::uint32>(n - 1)));
+ }
+ } else if (type == 4) {
+ send_lambda(next_actor, [=, ptr = next_actor.get_actor_unsafe()] { ptr->pass(n - 1); });
+ }
+ }
+ }
+
+ void raw_event(const td::Event::Raw &raw) override {
+ pass(static_cast<int>(raw.u32));
+ }
+
+ void start_up() override {
+ yield();
+ }
+ void wakeup() override {
+ 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() override {
+ scheduler_ = new td::ConcurrentScheduler();
+ scheduler_->init(thread_n_);
+
+ actor_array_ = std::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) override {
+ // first actor is on main_thread
+ actor_array_[0].get_actor_unsafe()->start_n = std::max(n, 100);
+ while (scheduler_->run_main(10)) {
+ // empty
+ }
+ }
+
+ void tear_down() override {
+ scheduler_->finish();
+ delete scheduler_;
+ }
+};
+
+template <int type>
+class QueryBench : public td::Benchmark {
+ public:
+ std::string get_description() const override {
+ 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 : 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(std::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:
+ std::unique_ptr<Callback> callback_;
+ };
+
+ class ServerActor : public td::Actor {
+ public:
+ class ClientCallback : public ClientActor::Callback {
+ public:
+ explicit ClientCallback(td::ActorId<ServerActor> server) : server_(server) {
+ }
+ void on_result(int x) override {
+ send_closure(server_, &ServerActor::on_result, x);
+ }
+
+ private:
+ td::ActorId<ServerActor> server_;
+ };
+ void start_up() override {
+ 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() override {
+ 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));
+ 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_; });
+ } else if (type == 5) {
+ send_closure(client_, &ClientActor::f_promise,
+ td::PromiseCreator::lambda(
+ [id = actor_id(this), n = n_](td::Unit) { send_closure(id, &ServerActor::result, n * n); }));
+ return;
+ }
+ }
+ }
+
+ void run(int n) {
+ n_ = n;
+ wakeup();
+ }
+
+ void raw_event(const td::Event::Raw &event) override {
+ 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_;
+ td::FutureActor<int> future_;
+ };
+
+ void start_up() override {
+ scheduler_ = new td::ConcurrentScheduler();
+ scheduler_->init(0);
+
+ server_ = scheduler_->create_actor_unsafe<ServerActor>(0, "Server");
+ scheduler_->start();
+ }
+
+ void run(int n) override {
+ // first actor is on main_thread
+ {
+ auto guard = scheduler_->get_current_guard();
+ send_closure(server_, &ServerActor::run, n);
+ }
+ while (scheduler_->run_main(10)) {
+ // empty
+ }
+ }
+
+ void tear_down() override {
+ server_.release();
+ scheduler_->finish();
+ delete scheduler_;
+ }
+
+ private:
+ td::ConcurrentScheduler *scheduler_;
+ td::ActorOwn<ServerActor> server_;
+};
+
+int main() {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
+ 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<2>());
+ bench(QueryBench<3>());
+ 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));
+ return 0;
+}
diff --git a/libs/tdlib/td/benchmark/bench_crypto.cpp b/libs/tdlib/td/benchmark/bench_crypto.cpp
new file mode 100644
index 0000000000..44d309ef11
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_crypto.cpp
@@ -0,0 +1,213 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/crypto.h"
+#include "td/utils/int_types.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/Random.h"
+#include "td/utils/Slice.h"
+
+#include <openssl/sha.h>
+
+#include <array>
+#include <atomic>
+#include <cstdint>
+#include <cstdlib>
+#include <random>
+#include <string>
+#include <vector>
+
+static constexpr int DATA_SIZE = 8 << 10;
+
+class SHA1Bench : public td::Benchmark {
+ public:
+ alignas(64) unsigned char data[DATA_SIZE];
+
+ std::string get_description() const override {
+ return PSTRING() << "SHA1 OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
+ }
+
+ void start_up() override {
+ for (int i = 0; i < DATA_SIZE; i++) {
+ data[i] = 123;
+ data[i] = 0;
+ }
+ }
+
+ void run(int n) override {
+ for (int i = 0; i < n; i++) {
+ unsigned char md[20];
+ SHA1(data, DATA_SIZE, md);
+ }
+ }
+};
+
+class AESBench : public td::Benchmark {
+ public:
+ alignas(64) unsigned char data[DATA_SIZE];
+ td::UInt256 key;
+ td::UInt256 iv;
+
+ std::string get_description() const override {
+ return PSTRING() << "AES OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
+ }
+
+ void start_up() override {
+ for (int i = 0; i < DATA_SIZE; i++) {
+ data[i] = 123;
+ }
+ td::Random::secure_bytes(key.raw, sizeof(key));
+ td::Random::secure_bytes(iv.raw, sizeof(iv));
+ }
+
+ void run(int n) override {
+ td::MutableSlice data_slice(data, DATA_SIZE);
+ for (int i = 0; i < n; i++) {
+ td::aes_ige_encrypt(key, &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;
+ for (int i = 0; i < 3; i++) {
+ v.push_back(td::thread([&] {
+ 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 : public td::Benchmark {
+ public:
+ alignas(64) unsigned char data[DATA_SIZE];
+
+ std::string get_description() const override {
+ return PSTRING() << "Crc32 zlib [" << (DATA_SIZE >> 10) << "KB]";
+ }
+
+ void start_up() override {
+ for (int i = 0; i < DATA_SIZE; i++) {
+ data[i] = 123;
+ data[i] = 0;
+ }
+ }
+
+ void run(int n) override {
+ 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 : public td::Benchmark {
+ public:
+ alignas(64) unsigned char data[DATA_SIZE];
+
+ std::string get_description() const override {
+ return PSTRING() << "Crc64 Anton [" << (DATA_SIZE >> 10) << "KB]";
+ }
+
+ void start_up() override {
+ for (int i = 0; i < DATA_SIZE; i++) {
+ data[i] = 123;
+ data[i] = 0;
+ }
+ }
+
+ void run(int n) override {
+ 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::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());
+ td::bench(SHA1Bench());
+ td::bench(AESBench());
+ td::bench(Crc32Bench());
+ td::bench(Crc64Bench());
+ return 0;
+}
diff --git a/libs/tdlib/td/benchmark/bench_db.cpp b/libs/tdlib/td/benchmark/bench_db.cpp
new file mode 100644
index 0000000000..dc768e9d9d
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_db.cpp
@@ -0,0 +1,237 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/db/binlog/Binlog.h"
+#include "td/db/BinlogKeyValue.h"
+#include "td/db/SeqKeyValue.h"
+#include "td/db/SqliteDb.h"
+#include "td/db/SqliteKeyValueAsync.h"
+
+#include "td/utils/benchmark.h"
+#include "td/utils/format.h"
+#include "td/utils/logging.h"
+#include "td/utils/Status.h"
+#include "td/utils/StringBuilder.h"
+
+#include <memory>
+
+template <class KeyValueT>
+class TdKvBench : public td::Benchmark {
+ td::ConcurrentScheduler sched;
+ td::string name_;
+
+ public:
+ explicit TdKvBench(td::string name) {
+ name_ = std::move(name);
+ }
+
+ td::string get_description() const override {
+ return name_;
+ }
+
+ class Main : public td::Actor {
+ public:
+ explicit Main(int n) : n_(n) {
+ }
+
+ private:
+ void loop() override {
+ KeyValueT::destroy("test_tddb").ignore();
+
+ class Worker : public Actor {
+ public:
+ Worker(int n, td::string db_name) : n_(n) {
+ kv_.init(db_name).ensure();
+ }
+
+ private:
+ void loop() override {
+ 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) override {
+ sched.init(1);
+ sched.create_actor_unsafe<Main>(1, "Main", n).release();
+ }
+
+ void run(int n) override {
+ sched.start();
+ while (sched.run_main(10)) {
+ // empty
+ }
+ sched.finish();
+ }
+
+ void tear_down() override {
+ }
+};
+
+template <bool is_encrypted = false>
+class SqliteKVBench : public td::Benchmark {
+ td::SqliteDb db;
+ td::string get_description() const override {
+ return PSTRING() << "SqliteKV " << td::tag("is_encrypted", is_encrypted);
+ }
+ void start_up() override {
+ td::string path = "testdb.sqlite";
+ td::SqliteDb::destroy(path).ignore();
+ if (is_encrypted) {
+ td::SqliteDb::change_key(path, td::DbKey::password("cucumber"), td::DbKey::empty());
+ db = td::SqliteDb::open_with_key(path, td::DbKey::password("cucumber")).move_as_ok();
+ } else {
+ db = td::SqliteDb::open_with_key(path, 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) override {
+ 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 : public td::Benchmark {
+ public:
+ td::string get_description() const override {
+ return "SqliteKeyValueAsync";
+ }
+ void start_up() override {
+ do_start_up().ensure();
+ scheduler_->start();
+ }
+ void run(int n) override {
+ auto guard = scheduler_->get_current_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() override {
+ scheduler_->run_main(0.1);
+ {
+ auto guard = scheduler_->get_current_guard();
+ sqlite_kv_async_.reset();
+ sqlite_kv_safe_.reset();
+ sql_connection_->close_and_destroy();
+ }
+
+ scheduler_->finish();
+ scheduler_.reset();
+ }
+
+ private:
+ std::unique_ptr<td::ConcurrentScheduler> scheduler_;
+ std::shared_ptr<td::SqliteConnectionSafe> sql_connection_;
+ std::shared_ptr<td::SqliteKeyValueSafe> sqlite_kv_safe_;
+ std::unique_ptr<td::SqliteKeyValueAsyncInterface> sqlite_kv_async_;
+
+ td::Status do_start_up() {
+ scheduler_ = std::make_unique<td::ConcurrentScheduler>();
+ scheduler_->init(1);
+
+ auto guard = scheduler_->get_current_guard();
+
+ td::string sql_db_name = "testdb.sqlite";
+ td::SqliteDb::destroy(sql_db_name).ignore();
+
+ sql_connection_ = std::make_shared<td::SqliteConnectionSafe>(sql_db_name);
+ 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 : public td::Benchmark {
+ td::string get_description() const override {
+ return "SeqKvBench";
+ }
+
+ td::SeqKeyValue kv;
+ void run(int n) override {
+ for (int i = 0; i < n; i++) {
+ kv.set(td::to_string(i % 10), td::to_string(i));
+ }
+ }
+};
+
+template <bool is_encrypted = false>
+class BinlogKeyValueBench : public td::Benchmark {
+ td::string get_description() const override {
+ return PSTRING() << "BinlogKeyValue " << td::tag("is_encrypted", is_encrypted);
+ }
+
+ td::BinlogKeyValue<td::Binlog> kv;
+ void start_up() override {
+ td::SqliteDb::destroy("test_binlog").ignore();
+ kv.init("test_binlog", is_encrypted ? td::DbKey::password("cucumber") : td::DbKey::empty()).ensure();
+ }
+ void run(int n) override {
+ 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(BinlogKeyValueBench<true>());
+ bench(BinlogKeyValueBench<false>());
+ bench(SqliteKVBench<false>());
+ bench(SqliteKVBench<true>());
+ bench(SqliteKeyValueAsyncBench());
+ bench(TdKvBench<td::BinlogKeyValue<td::Binlog>>("BinlogKeyValue<Binlog>"));
+ bench(TdKvBench<td::BinlogKeyValue<td::ConcurrentBinlog>>("BinlogKeyValue<ConcurrentBinlog>"));
+ bench(SeqKvBench());
+ return 0;
+}
diff --git a/libs/tdlib/td/benchmark/bench_empty.cpp b/libs/tdlib/td/benchmark/bench_empty.cpp
new file mode 100644
index 0000000000..f6718152b4
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_empty.cpp
@@ -0,0 +1,9 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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() {
+ return 0;
+}
diff --git a/libs/tdlib/td/benchmark/bench_handshake.cpp b/libs/tdlib/td/benchmark/bench_handshake.cpp
new file mode 100644
index 0000000000..08d04f009c
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_handshake.cpp
@@ -0,0 +1,76 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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" // for bench, do_not_optimize_away, etc
+
+#include "td/mtproto/crypto.h"
+
+#include "td/utils/base64.h"
+#include "td/utils/logging.h"
+#include "td/utils/Slice.h"
+
+#include <map>
+
+#if TD_LINUX || TD_ANDROID || TD_TIZEN
+#include <semaphore.h>
+#endif
+
+namespace td {
+
+static int32 g = 3;
+static string prime_base64 =
+ "xxyuucaxyQSObFIvcPE_c5gNQCOOPiHBSTTQN1Y9kw9IGYoKp8FAWCKUk9IlMPTb-jNvbgrJJROVQ67UTM58NyD9UfaUWHBaxozU_mtrE6vcl0ZRKW"
+ "kyhFTxj6-MWV9kJHf-lrsqlB1bzR1KyMxJiAcI-ps3jjxPOpBgvuZ8-aSkppWBEFGQfhYnU7VrD2tBDbp02KhLKhSzFE4O8ShHVP0X7ZUNWWW0ud1G"
+ "WC2xF40WnGvEZbDW_5yjko_vW5rk5Bj8Feg-vqD4f6n_Xu1wBQ3tKEn0e_lZ2VaFDOkphR8NgRX2NbEF7i5OFdBLJFS_b0-t8DSxBAMRnNjjuS_MW"
+ "w";
+
+class HandshakeBench : public Benchmark {
+ std::string get_description() const override {
+ return "Handshake";
+ }
+
+ class FakeDhCallback : public DhCallback {
+ public:
+ int is_good_prime(Slice prime_str) const override {
+ auto it = cache.find(prime_str.str());
+ if (it == cache.end()) {
+ return -1;
+ }
+ return it->second;
+ }
+ void add_good_prime(Slice prime_str) const override {
+ cache[prime_str.str()] = 1;
+ }
+ void add_bad_prime(Slice prime_str) const override {
+ cache[prime_str.str()] = 0;
+ }
+ mutable std::map<string, int> cache;
+ } dh_callback;
+
+ void run(int n) override {
+ DhHandshake a;
+ DhHandshake b;
+ auto prime = base64url_decode(prime_base64).move_as_ok();
+ 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(&dh_callback).ensure();
+ b.run_checks(&dh_callback).ensure();
+ auto a_key = a.gen_key();
+ auto b_key = b.gen_key();
+ CHECK(a_key.first == b_key.first);
+ }
+ }
+};
+} // namespace td
+
+int main() {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
+ td::bench(td::HandshakeBench());
+ return 0;
+}
diff --git a/libs/tdlib/td/benchmark/bench_http.cpp b/libs/tdlib/td/benchmark/bench_http.cpp
new file mode 100644
index 0000000000..6958a5b313
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_http.cpp
@@ -0,0 +1,78 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/net/HttpOutboundConnection.h"
+#include "td/net/HttpQuery.h"
+
+#include "td/utils/buffer.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>
+
+namespace td {
+
+std::atomic<int> counter;
+class HttpClient : public HttpOutboundConnection::Callback {
+ void start_up() override {
+ IPAddress addr;
+ addr.init_ipv4_port("127.0.0.1", 8082).ensure();
+ auto fd = SocketFd::open(addr);
+ CHECK(fd.is_ok()) << fd.error();
+ connection_ =
+ create_actor<HttpOutboundConnection>("Connect", fd.move_as_ok(), std::numeric_limits<size_t>::max(), 0, 0,
+ ActorOwn<HttpOutboundConnection::Callback>(actor_id(this)));
+ yield();
+ cnt_ = 100000;
+ counter++;
+ }
+ void tear_down() override {
+ if (--counter == 0) {
+ Scheduler::instance()->finish();
+ }
+ }
+ void loop() override {
+ if (cnt_-- < 0) {
+ return stop();
+ }
+ send_closure(connection_, &HttpOutboundConnection::write_next, BufferSlice("GET / HTTP/1.1\r\n\r\n"));
+ send_closure(connection_, &HttpOutboundConnection::write_ok);
+ LOG(INFO) << "SEND";
+ }
+ void handle(HttpQueryPtr result) override {
+ loop();
+ }
+ void on_connection_error(Status error) override {
+ LOG(ERROR) << "ERROR: " << error;
+ }
+
+ ActorOwn<HttpOutboundConnection> connection_;
+ int cnt_;
+};
+
+int main() {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
+ auto scheduler = make_unique<ConcurrentScheduler>();
+ scheduler->init(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();
+ return 0;
+}
+} // namespace td
+
+int main() {
+ return td::main();
+}
diff --git a/libs/tdlib/td/benchmark/bench_http_reader.cpp b/libs/tdlib/td/benchmark/bench_http_reader.cpp
new file mode 100644
index 0000000000..2afe2d73ff
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_http_reader.cpp
@@ -0,0 +1,118 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/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 : public td::Benchmark {
+ std::string get_description() const override {
+ return "HttpReaderBench";
+ }
+
+ void run(int n) override {
+ int 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() override {
+ writer_ = td::ChainBufferWriter::create_empty();
+ reader_ = writer_.extract_reader();
+ http_reader_.init(&reader_, 10000, 0);
+ }
+};
+
+class BufferBench : public td::Benchmark {
+ std::string get_description() const override {
+ return "BufferBench";
+ }
+
+ void run(int n) override {
+ int 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++) {
+ reader_.cut_head(http_query.size());
+ }
+ }
+ }
+ td::ChainBufferWriter writer_;
+ td::ChainBufferReader reader_;
+ td::HttpReader http_reader_;
+
+ void start_up() override {
+ writer_ = td::ChainBufferWriter::create_empty();
+ reader_ = writer_.extract_reader();
+ }
+};
+
+class FindBoundaryBench : public td::Benchmark {
+ std::string get_description() const override {
+ return "FindBoundaryBench";
+ }
+
+ void run(int n) override {
+ int 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());
+ reader_.cut_head(len + 2);
+ reader_.advance(2);
+ }
+ }
+ }
+ td::ChainBufferWriter writer_;
+ td::ChainBufferReader reader_;
+ td::HttpReader http_reader_;
+
+ void start_up() override {
+ writer_ = td::ChainBufferWriter::create_empty();
+ reader_ = writer_.extract_reader();
+ }
+};
+
+int main() {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
+ td::bench(BufferBench());
+ td::bench(FindBoundaryBench());
+ td::bench(HttpReaderBench());
+}
diff --git a/libs/tdlib/td/benchmark/bench_http_server.cpp b/libs/tdlib/td/benchmark/bench_http_server.cpp
new file mode 100644
index 0000000000..c48e8b4a67
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_http_server.cpp
@@ -0,0 +1,89 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/net/HttpHeaderCreator.h"
+#include "td/net/HttpInboundConnection.h"
+#include "td/net/HttpQuery.h"
+#include "td/net/TcpListener.h"
+
+#include "td/utils/buffer.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/SocketFd.h"
+#include "td/utils/Slice.h"
+
+namespace td {
+
+static int cnt = 0;
+class HelloWorld : public HttpInboundConnection::Callback {
+ public:
+ void handle(HttpQueryPtr query, ActorOwn<HttpInboundConnection> connection) override {
+ // LOG(ERROR) << *query;
+ HttpHeaderCreator hc;
+ Slice content = "hello world";
+ //auto content = 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, &HttpInboundConnection::write_next, BufferSlice(res.ok()));
+ send_closure(connection.release(), &HttpInboundConnection::write_ok);
+ }
+ void hangup() override {
+ LOG(ERROR) << "CLOSE " << cnt--;
+ stop();
+ }
+};
+
+const int N = 0;
+class Server : public TcpListener::Callback {
+ public:
+ void start_up() override {
+ listener_ = create_actor<TcpListener>("Listener", 8082, ActorOwn<TcpListener::Callback>(actor_id(this)));
+ }
+ void accept(SocketFd fd) override {
+ LOG(ERROR) << "ACCEPT " << cnt++;
+ pos_++;
+ auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
+ create_actor_on_scheduler<HttpInboundConnection>("HttpInboundConnection", scheduler_id, std::move(fd), 1024 * 1024,
+ 0, 0,
+ create_actor_on_scheduler<HelloWorld>("HelloWorld", scheduler_id))
+ .release();
+ }
+ void hangup() override {
+ // may be it should be default?..
+ LOG(ERROR) << "hangup..";
+ stop();
+ }
+
+ private:
+ ActorOwn<TcpListener> listener_;
+ int pos_{0};
+};
+
+int main() {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
+ auto scheduler = make_unique<ConcurrentScheduler>();
+ scheduler->init(N);
+ scheduler->create_actor_unsafe<Server>(0, "Server").release();
+ scheduler->start();
+ while (scheduler->run_main(10)) {
+ // empty
+ }
+ scheduler->finish();
+ return 0;
+}
+} // namespace td
+
+int main() {
+ return td::main();
+}
diff --git a/libs/tdlib/td/benchmark/bench_http_server_cheat.cpp b/libs/tdlib/td/benchmark/bench_http_server_cheat.cpp
new file mode 100644
index 0000000000..da6fbbd713
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_http_server_cheat.cpp
@@ -0,0 +1,138 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/net/HttpHeaderCreator.h"
+#include "td/net/HttpInboundConnection.h"
+#include "td/net/TcpListener.h"
+
+#include "td/utils/buffer.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/Fd.h"
+#include "td/utils/port/SocketFd.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
+
+#include <array>
+
+namespace td {
+
+// HttpInboundConnection header
+static int cnt = 0;
+class HelloWorld : public Actor {
+ public:
+ explicit HelloWorld(SocketFd socket_fd) : socket_fd_(std::move(socket_fd)) {
+ }
+
+ private:
+ SocketFd socket_fd_;
+
+ std::array<char, 1024> read_buf;
+ size_t read_new_lines{0};
+
+ std::string hello_;
+ std::string write_buf_;
+ size_t write_pos_{0};
+
+ void start_up() override {
+ socket_fd_.get_fd().set_observer(this);
+ subscribe(socket_fd_.get_fd());
+ HttpHeaderCreator hc;
+ Slice content = "hello world";
+ //auto content = 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() override {
+ auto status = do_loop();
+ if (status.is_error()) {
+ unsubscribe(socket_fd_.get_fd());
+ stop();
+ LOG(ERROR) << "CLOSE: " << status;
+ }
+ }
+ Status do_loop() {
+ TRY_STATUS(read_loop());
+ TRY_STATUS(write_loop());
+ if (can_close(socket_fd_)) {
+ return Status::Error("CLOSE");
+ }
+ return Status::OK();
+ }
+ Status write_loop() {
+ while (can_write(socket_fd_) && write_pos_ < write_buf_.size()) {
+ TRY_RESULT(written, socket_fd_.write(Slice(write_buf_).substr(write_pos_)));
+ write_pos_ += written;
+ if (write_pos_ == write_buf_.size()) {
+ write_pos_ = 0;
+ write_buf_.clear();
+ }
+ }
+ return Status::OK();
+ }
+ Status read_loop() {
+ while (can_read(socket_fd_)) {
+ TRY_RESULT(read_size, socket_fd_.read(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 Status::OK();
+ }
+};
+const int N = 0;
+class Server : public TcpListener::Callback {
+ public:
+ void start_up() override {
+ listener_ = create_actor<TcpListener>("Listener", 8082, ActorOwn<TcpListener::Callback>(actor_id(this)));
+ }
+ void accept(SocketFd fd) override {
+ LOG(ERROR) << "ACCEPT " << cnt++;
+ pos_++;
+ auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
+ create_actor_on_scheduler<HelloWorld>("HttpInboundConnection", scheduler_id, std::move(fd)).release();
+ }
+ void hangup() override {
+ // may be it should be default?..
+ LOG(ERROR) << "hangup..";
+ stop();
+ }
+
+ private:
+ ActorOwn<TcpListener> listener_;
+ int pos_{0};
+};
+
+int main() {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
+ auto scheduler = make_unique<ConcurrentScheduler>();
+ scheduler->init(N);
+ scheduler->create_actor_unsafe<Server>(0, "Server").release();
+ scheduler->start();
+ while (scheduler->run_main(10)) {
+ // empty
+ }
+ scheduler->finish();
+ return 0;
+}
+} // namespace td
+
+int main() {
+ return td::main();
+}
diff --git a/libs/tdlib/td/benchmark/bench_http_server_fast.cpp b/libs/tdlib/td/benchmark/bench_http_server_fast.cpp
new file mode 100644
index 0000000000..fbda47590b
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_http_server_fast.cpp
@@ -0,0 +1,121 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/net/HttpHeaderCreator.h"
+#include "td/net/HttpQuery.h"
+#include "td/net/HttpReader.h"
+#include "td/net/TcpListener.h"
+
+#include "td/utils/buffer.h"
+#include "td/utils/BufferedFd.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/Fd.h"
+#include "td/utils/port/SocketFd.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
+
+namespace td {
+
+class HttpEchoConnection : public Actor {
+ public:
+ explicit HttpEchoConnection(SocketFd fd) : fd_(std::move(fd)) {
+ }
+
+ private:
+ BufferedFd<SocketFd> fd_;
+ HttpReader reader_;
+ HttpQuery query_;
+ void start_up() override {
+ fd_.get_fd().set_observer(this);
+ subscribe(fd_.get_fd());
+ reader_.init(&fd_.input_buffer(), 1024 * 1024, 0);
+ }
+
+ void handle_query() {
+ query_ = HttpQuery();
+ HttpHeaderCreator hc;
+ Slice content = "hello world";
+ //auto content = 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() override {
+ auto status = [&] {
+ TRY_STATUS(loop_read());
+ TRY_STATUS(loop_write());
+ return Status::OK();
+ }();
+ if (status.is_error() || can_close(fd_)) {
+ stop();
+ }
+ }
+ Status loop_read() {
+ if (can_read(fd_)) {
+ TRY_STATUS(fd_.flush_read());
+ }
+ while (true) {
+ TRY_RESULT(need, reader_.read_next(&query_));
+ if (need == 0) {
+ handle_query();
+ } else {
+ break;
+ }
+ }
+ return Status::OK();
+ }
+ Status loop_write() {
+ TRY_STATUS(fd_.flush_write());
+ return Status::OK();
+ }
+};
+
+const int N = 4;
+class Server : public TcpListener::Callback {
+ public:
+ void start_up() override {
+ listener_ = create_actor<TcpListener>("Listener", 8082, ActorOwn<TcpListener::Callback>(actor_id(this)));
+ }
+ void accept(SocketFd fd) override {
+ pos_++;
+ auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
+ create_actor_on_scheduler<HttpEchoConnection>("HttpInboundConnection", scheduler_id, std::move(fd)).release();
+ }
+ void hangup() override {
+ LOG(ERROR) << "hangup..";
+ stop();
+ }
+
+ private:
+ ActorOwn<TcpListener> listener_;
+ int pos_{0};
+};
+
+int main() {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
+ auto scheduler = make_unique<ConcurrentScheduler>();
+ scheduler->init(N);
+ scheduler->create_actor_unsafe<Server>(0, "Server").release();
+ scheduler->start();
+ while (scheduler->run_main(10)) {
+ // empty
+ }
+ scheduler->finish();
+ return 0;
+}
+} // namespace td
+
+int main() {
+ return td::main();
+}
diff --git a/libs/tdlib/td/benchmark/bench_log.cpp b/libs/tdlib/td/benchmark/bench_log.cpp
new file mode 100644
index 0000000000..a57b1b9b42
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_log.cpp
@@ -0,0 +1,163 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/logging.h"
+
+#include <cstdio>
+#include <fstream>
+#include <iostream>
+#include <mutex>
+#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 : public td::Benchmark {
+ protected:
+ std::string file_name_;
+ std::ofstream stream;
+ enum { buffer_size = 1 << 20 };
+ char buffer[buffer_size];
+
+ public:
+ std::string get_description() const override {
+ return "ostream (to file, no buf, no flush)";
+ }
+
+ void start_up() override {
+ 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) override {
+ for (int i = 0; i < n; i++) {
+ stream << "This is just for test" << 987654321 << '\n';
+ }
+ }
+
+ void tear_down() override {
+ stream.close();
+ unlink(file_name_.c_str());
+ }
+};
+
+class FILEWriteBench : public td::Benchmark {
+ protected:
+ std::string file_name_;
+ FILE *file;
+ enum { buffer_size = 1 << 20 };
+ char buffer[buffer_size];
+
+ public:
+ std::string get_description() const override {
+ return "std::fprintf (to file, no buf, no flush)";
+ }
+
+ void start_up() override {
+ file_name_ = create_tmp_file();
+ file = fopen(file_name_.c_str(), "w");
+ // setvbuf(file, buffer, _IOFBF, buffer_size);
+ }
+
+ void run(int n) override {
+ for (int i = 0; i < n; i++) {
+ std::fprintf(file, "This is just for test%d\n", 987654321);
+ // std::fflush(file);
+ }
+ }
+
+ void tear_down() override {
+ 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 : public td::Benchmark {
+ public:
+ std::string get_description() const override {
+ return "android_log";
+ }
+ void start_up() override {
+ }
+ void run(int n) override {
+ for (int i = 0; i < n; i++) {
+ ALOG("This is just for test%d\n", 987654321);
+ }
+ }
+ void tear_down() override {
+ }
+};
+#endif
+
+class LogWriteBench : public td::Benchmark {
+ protected:
+ std::string file_name_;
+ std::ofstream stream;
+ std::streambuf *old_buf;
+ enum { buffer_size = 1 << 20 };
+ char buffer[buffer_size];
+
+ public:
+ std::string get_description() const override {
+ return "td_log (slow in debug mode)";
+ }
+
+ void start_up() override {
+ 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) override {
+ for (int i = 0; i < n; i++) {
+ LOG(DEBUG) << "This is just for test" << 987654321;
+ }
+ }
+
+ void tear_down() override {
+ stream.close();
+ unlink(file_name_.c_str());
+ std::cerr.rdbuf(old_buf);
+ }
+};
+
+std::mutex mutex;
+
+int main() {
+ td::bench(LogWriteBench());
+#if TD_ANDROID
+ td::bench(ALogWriteBench());
+#endif
+ td::bench(IostreamWriteBench());
+ td::bench(FILEWriteBench());
+ return 0;
+}
diff --git a/libs/tdlib/td/benchmark/bench_misc.cpp b/libs/tdlib/td/benchmark/bench_misc.cpp
new file mode 100644
index 0000000000..bfbcea438b
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_misc.cpp
@@ -0,0 +1,392 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/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/Slice.h"
+
+#include "td/telegram/telegram_api.h"
+#include "td/telegram/telegram_api.hpp"
+
+#if !TD_WINDOWS
+#include <unistd.h>
+#include <utime.h>
+#endif
+
+#if TD_LINUX || TD_ANDROID || TD_TIZEN
+#include <semaphore.h>
+#endif
+
+#include <atomic>
+#include <cstdint>
+
+namespace td {
+
+class F {
+ uint32 &sum;
+
+ public:
+ explicit F(uint32 &sum) : sum(sum) {
+ }
+
+ template <class T>
+ void operator()(const T &x) const {
+ sum += static_cast<uint32>(x.get_id());
+ }
+};
+
+BENCH(Call, "TL Call") {
+ tl_object_ptr<telegram_api::Function> x = make_tl_object<telegram_api::account_getWallPapers>();
+ uint32 res = 0;
+ F f(res);
+ for (int i = 0; i < n; i++) {
+ downcast_call(*x, f);
+ }
+ do_not_optimize_away(res);
+}
+
+#if !TD_EVENTFD_UNSUPPORTED
+BENCH(EventFd, "EventFd") {
+ 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;
+ }
+ do_not_optimize_away(res);
+}
+
+BENCH(NewObj, "new struct then delete") {
+ struct A {
+ int32 a = 0;
+ int32 b = 0;
+ int32 c = 0;
+ 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;
+ do_not_optimize_away(res);
+}
+
+#if !TD_THREAD_UNSUPPORTED
+BENCH(ThreadNew, "new struct then delete in several threads") {
+ td::NewObjBench a, b;
+ thread ta([&] { a.run(n / 2); });
+ thread tb([&] { b.run(n - n / 2); });
+ ta.join();
+ tb.join();
+}
+#endif
+
+// Too hard for android clang (?)
+BENCH(Time, "Clocks::monotonic") {
+ double res = 0;
+ for (int i = 0; i < n; i++) {
+ res += Clocks::monotonic();
+ }
+ do_not_optimize_away(res);
+}
+
+#if !TD_WINDOWS
+class PipeBench : public Benchmark {
+ public:
+ int p[2];
+
+ PipeBench() {
+ pipe(p);
+ }
+
+ string get_description() const override {
+ return "pipe write + read int32";
+ }
+
+ void start_up() override {
+ pipe(p);
+ }
+
+ void run(int n) override {
+ int res = 0;
+ for (int i = 0; i < n; i++) {
+ int val = 1;
+ write(p[1], &val, sizeof(val));
+ read(p[0], &val, sizeof(val));
+ res += val;
+ }
+ do_not_optimize_away(res);
+ }
+
+ void tear_down() override {
+ close(p[0]);
+ close(p[1]);
+ }
+};
+#endif
+
+#if TD_LINUX || TD_ANDROID || TD_TIZEN
+class SemBench : public Benchmark {
+ sem_t sem;
+
+ public:
+ string get_description() const override {
+ return "sem post + wait";
+ }
+
+ void start_up() override {
+ int err = sem_init(&sem, 0, 0);
+ CHECK(err != -1);
+ }
+
+ void run(int n) override {
+ for (int i = 0; i < n; i++) {
+ sem_post(&sem);
+ sem_wait(&sem);
+ }
+ }
+
+ void tear_down() override {
+ sem_destroy(&sem);
+ }
+};
+#endif
+
+#if !TD_WINDOWS
+class UtimeBench : public Benchmark {
+ public:
+ void start_up() override {
+ FileFd::open("test", FileFd::Flags::Create | FileFd::Flags::Write).move_as_ok().close();
+ }
+ string get_description() const override {
+ return "utime";
+ }
+ void run(int n) override {
+ 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 = FileFd::open("test", FileFd::Flags::Create | FileFd::Flags::Write).move_as_ok();
+ for (int i = 0; i < n; i++) {
+ fd.pwrite("a", 0).ok();
+ }
+ fd.close();
+}
+
+class CreateFileBench : public Benchmark {
+ string get_description() const override {
+ return "create_file";
+ }
+ void start_up() override {
+ mkdir("A").ensure();
+ }
+ void run(int n) override {
+ for (int i = 0; i < n; i++) {
+ FileFd::open("A/" + to_string(i), FileFd::Flags::Write | FileFd::Flags::Create).move_as_ok().close();
+ }
+ }
+ void tear_down() override {
+ auto status = td::walk_path("A/", [&](CSlice path, bool is_dir) {
+ if (is_dir) {
+ rmdir(path).ignore();
+ } else {
+ unlink(path).ignore();
+ }
+ });
+ }
+};
+class WalkPathBench : public Benchmark {
+ string get_description() const override {
+ return "walk_path";
+ }
+ void start_up_n(int n) override {
+ mkdir("A").ensure();
+ for (int i = 0; i < n; i++) {
+ FileFd::open("A/" + to_string(i), FileFd::Flags::Write | FileFd::Flags::Create).move_as_ok().close();
+ }
+ }
+ void run(int n) override {
+ int cnt = 0;
+ auto status = td::walk_path("A/", [&](CSlice path, bool is_dir) {
+ stat(path).ok();
+ cnt++;
+ });
+ }
+ void tear_down() override {
+ auto status = td::walk_path("A/", [&](CSlice path, bool is_dir) {
+ if (is_dir) {
+ rmdir(path).ignore();
+ } else {
+ unlink(path).ignore();
+ }
+ });
+ }
+};
+
+#if !TD_THREAD_UNSUPPORTED
+template <int ThreadN = 2>
+class AtomicReleaseIncBench : public Benchmark {
+ string get_description() const override {
+ return PSTRING() << "AtomicReleaseInc" << ThreadN;
+ }
+
+ static std::atomic<uint64> a_;
+ void run(int n) override {
+ std::vector<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<uint64> AtomicReleaseIncBench<ThreadN>::a_;
+
+template <int ThreadN = 2>
+class AtomicReleaseCasIncBench : public Benchmark {
+ string get_description() const override {
+ return PSTRING() << "AtomicReleaseCasInc" << ThreadN;
+ }
+
+ static std::atomic<uint64> a_;
+ void run(int n) override {
+ std::vector<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<uint64> AtomicReleaseCasIncBench<ThreadN>::a_;
+
+template <int ThreadN = 2>
+class RwMutexReadBench : public Benchmark {
+ string get_description() const override {
+ return PSTRING() << "RwMutexRead" << ThreadN;
+ }
+ RwMutex mutex_;
+ void run(int n) override {
+ std::vector<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 = 2>
+class RwMutexWriteBench : public Benchmark {
+ string get_description() const override {
+ return PSTRING() << "RwMutexWrite" << ThreadN;
+ }
+ RwMutex mutex_;
+ void run(int n) override {
+ std::vector<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();
+ }
+ }
+};
+#endif
+} // namespace td
+
+int main() {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
+#if !TD_THREAD_UNSUPPORTED
+ td::bench(td::AtomicReleaseIncBench<1>());
+ td::bench(td::AtomicReleaseIncBench<2>());
+ td::bench(td::AtomicReleaseCasIncBench<1>());
+ td::bench(td::AtomicReleaseCasIncBench<2>());
+ td::bench(td::RwMutexWriteBench<1>());
+ td::bench(td::RwMutexReadBench<1>());
+ td::bench(td::RwMutexWriteBench<>());
+ td::bench(td::RwMutexReadBench<>());
+#endif
+#if !TD_WINDOWS
+ td::bench(td::UtimeBench());
+#endif
+ td::bench(td::WalkPathBench());
+ td::bench(td::CreateFileBench());
+ td::bench(td::PwriteBench());
+
+ td::bench(td::CallBench());
+#if !TD_THREAD_UNSUPPORTED
+ td::bench(td::ThreadNewBench());
+#endif
+#if !TD_EVENTFD_UNSUPPORTED
+ td::bench(td::EventFdBench());
+#endif
+ td::bench(td::NewObjBench());
+ td::bench(td::NewIntBench());
+#if !TD_WINDOWS
+ td::bench(td::PipeBench());
+#endif
+#if TD_LINUX || TD_ANDROID || TD_TIZEN
+ td::bench(td::SemBench());
+#endif
+ return 0;
+}
diff --git a/libs/tdlib/td/benchmark/bench_queue.cpp b/libs/tdlib/td/benchmark/bench_queue.cpp
new file mode 100644
index 0000000000..13288e6cd7
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_queue.cpp
@@ -0,0 +1,943 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/queue.h"
+
+// TODO: check system calls
+// TODO: all return values must be checked
+
+#include <atomic>
+#include <cstdio>
+#include <cstdlib>
+#include <vector>
+
+#include <pthread.h>
+#include <sched.h>
+#include <semaphore.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#if TD_LINUX
+#include <sys/eventfd.h>
+#endif
+
+using std::atomic;
+using std::vector;
+
+using td::int32;
+using td::uint32;
+
+#define MODE std::memory_order_relaxed
+
+// void set_affinity(int mask) {
+// int err, syscallres;
+// pid_t pid = gettid();
+// syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(mask), &mask);
+// if (syscallres) {
+// err = errno;
+// perror("oppa");
+//}
+//}
+
+// TODO: warnings and asserts. There should be no warnings or debug output in production.
+using qvalue_t = int;
+
+// Just for testing, not production
+class PipeQueue {
+ int input;
+ int output;
+
+ public:
+ void init() {
+ int new_pipe[2];
+ pipe(new_pipe);
+ output = new_pipe[0];
+ input = new_pipe[1];
+ }
+
+ void put(qvalue_t value) {
+ write(input, &value, sizeof(value));
+ }
+
+ qvalue_t get() {
+ qvalue_t res;
+ read(output, &res, sizeof(res));
+ return res;
+ }
+
+ void destroy() {
+ close(input);
+ close(output);
+ }
+};
+
+class Backoff {
+ int cnt;
+
+ public:
+ Backoff() : cnt(0) {
+ }
+
+ bool next() {
+ cnt++;
+ if (cnt < 50) {
+ return true;
+ } else {
+ sched_yield();
+ return cnt < 500;
+ }
+ }
+};
+
+class VarQueue {
+ atomic<qvalue_t> data;
+
+ 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();
+ }
+};
+
+#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;
+ write(fd, &x, sizeof(x));
+ }
+ qvalue_t get() {
+ td::int64 x;
+ read(fd, &x, sizeof(x));
+ 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(atomic<qvalue_t>)];
+ };
+ node q[queue_buf_size];
+
+ struct Position {
+ atomic<uint32> i;
+ char pad[64 - sizeof(atomic<uint32>)];
+
+ uint32 local_read_i;
+ uint32 local_write_i;
+ char pad2[64 - sizeof(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()) {
+ std::fprintf(stderr, "put strong failed\n");
+ std::exit(0);
+ }
+ put_unsafe(val);
+ }
+
+ void put(qvalue_t val) {
+ put_noflush(val);
+ flush_writer();
+ }
+
+ void destroy() {
+ }
+};
+
+#if TD_LINUX
+class BufferedFdQueue {
+ int fd;
+ atomic<int> wait_flag;
+ 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)) {
+ write(fd, &x, 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)) {
+ write(fd, &x, 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())) {
+ read(fd, &x, 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()) {
+ read(fd, &x, sizeof(x));
+ __sync_synchronize();
+ }
+ wait_flag.store(0, MODE);
+ return get_unsafe_flush();
+ }
+ void destroy() {
+ q.destroy();
+ close(fd);
+ }
+};
+
+class FdQueue {
+ int fd;
+ atomic<int> wait_flag;
+ 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)) {
+ write(fd, &x, sizeof(x));
+ }
+ }
+ qvalue_t get() {
+ // td::int64 x;
+ // read(fd, &x, 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();
+ // std::fprintf(stderr, "!\n");
+ // while (res == -1 && read(fd, &x, sizeof(x))) {
+ // res = q.try_get();
+ //}
+ do {
+ __sync_synchronize();
+ res = q.try_get();
+ } while (res == -1 && read(fd, &x, sizeof(x)));
+ q.acquire();
+ wait_flag.store(0, MODE);
+ return res;
+ }
+ void destroy() {
+ q.destroy();
+ close(fd);
+ }
+};
+#endif
+
+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 : public td::Benchmark {
+ QueueT client, server;
+ int connections_n, queries_n;
+
+ int server_active_connections;
+ int client_active_connections;
+ vector<td::int64> server_conn;
+ vector<td::int64> client_conn;
+
+ public:
+ explicit QueueBenchmark2(int connections_n = 1) : connections_n(connections_n) {
+ }
+
+ std::string get_description() const override {
+ return "QueueBenchmark2";
+ }
+
+ void start_up() override {
+ client.init();
+ server.init();
+ }
+
+ void tear_down() override {
+ client.destroy();
+ server.destroy();
+ }
+
+ void server_process(qvalue_t value) {
+ int no = value & 0x00FFFFFF;
+ int co = static_cast<int>(static_cast<unsigned int>(value) >> 24);
+ // std::fprintf(stderr, "-->%d %d\n", co, no);
+ if (co < 0 || co >= connections_n || no != server_conn[co]++) {
+ std::fprintf(stderr, "%d %d\n", co, no);
+ std::fprintf(stderr, "expected %d %lld\n", co, static_cast<long long>(server_conn[co] - 1));
+ std::fprintf(stderr, "Server BUG\n");
+ while (true) {
+ }
+ }
+ // std::fprintf(stderr, "no = %d/%d\n", no, queries_n);
+ // std::fprintf(stderr, "answer: %d %d\n", no, co);
+
+ client.writer_put(value);
+ client.writer_flush();
+ if (no + 1 >= queries_n) {
+ server_active_connections--;
+ }
+ }
+
+ void *server_run(void *) {
+ server_conn = vector<td::int64>(connections_n);
+ server_active_connections = connections_n;
+
+ while (server_active_connections > 0) {
+ int cnt = server.reader_wait();
+ if (cnt == 0) {
+ std::fprintf(stderr, "ERROR!\n");
+ std::exit(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;
+ int co = static_cast<int>(static_cast<unsigned int>(value) >> 24);
+ // std::fprintf(stderr, "<--%d %d\n", co, no);
+ if (co < 0 || co >= connections_n || no != client_conn[co]++) {
+ std::fprintf(stderr, "%d %d\n", co, no);
+ std::fprintf(stderr, "expected %d %lld\n", co, static_cast<long long>(client_conn[co] - 1));
+ std::fprintf(stderr, "BUG\n");
+ while (true) {
+ }
+ std::exit(0);
+ }
+ if (no + 1 < queries_n) {
+ // std::fprintf(stderr, "query: %d %d\n", no + 1, co);
+ server.writer_put(value + 1);
+ server.writer_flush();
+ } else {
+ client_active_connections--;
+ }
+ }
+
+ void *client_run(void *) {
+ client_conn = vector<td::int64>(connections_n);
+ client_active_connections = connections_n;
+ if (queries_n >= (1 << 24)) {
+ std::fprintf(stderr, "Too big queries_n\n");
+ std::exit(0);
+ }
+
+ 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();
+ if (cnt == 0) {
+ std::fprintf(stderr, "ERROR!\n");
+ std::exit(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) override {
+ 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 : public td::Benchmark {
+ QueueT client, server;
+ const int connections_n;
+ int queries_n;
+
+ public:
+ explicit QueueBenchmark(int connections_n = 1) : connections_n(connections_n) {
+ }
+
+ std::string get_description() const override {
+ return "QueueBenchmark";
+ }
+
+ void start_up() override {
+ client.init();
+ server.init();
+ }
+
+ void tear_down() override {
+ client.destroy();
+ server.destroy();
+ }
+
+ void *server_run(void *) {
+ vector<td::int64> conn(connections_n);
+ int active_connections = connections_n;
+ while (active_connections > 0) {
+ qvalue_t value = server.get();
+ int no = value & 0x00FFFFFF;
+ int co = static_cast<int>(value >> 24);
+ // std::fprintf(stderr, "-->%d %d\n", co, no);
+ if (co < 0 || co >= connections_n || no != conn[co]++) {
+ std::fprintf(stderr, "%d %d\n", co, no);
+ std::fprintf(stderr, "expected %d %lld\n", co, static_cast<long long>(conn[co] - 1));
+ std::fprintf(stderr, "Server BUG\n");
+ while (true) {
+ }
+ }
+ // std::fprintf(stderr, "no = %d/%d\n", no, queries_n);
+ client.put(value);
+ if (no + 1 >= queries_n) {
+ active_connections--;
+ }
+ }
+ return nullptr;
+ }
+
+ void *client_run(void *) {
+ vector<td::int64> conn(connections_n);
+ if (queries_n >= (1 << 24)) {
+ std::fprintf(stderr, "Too big queries_n\n");
+ std::exit(0);
+ }
+ 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;
+ int co = static_cast<int>(value >> 24);
+ // std::fprintf(stderr, "<--%d %d\n", co, no);
+ if (co < 0 || co >= connections_n || no != conn[co]++) {
+ std::fprintf(stderr, "%d %d\n", co, no);
+ std::fprintf(stderr, "expected %d %lld\n", co, static_cast<long long>(conn[co] - 1));
+ std::fprintf(stderr, "BUG\n");
+ while (true) {
+ }
+ std::exit(0);
+ }
+ 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 *) {
+ vector<td::int64> conn(connections_n);
+ if (queries_n >= (1 << 24)) {
+ std::fprintf(stderr, "Too big queries_n\n");
+ std::exit(0);
+ }
+ for (int it = 0; it < queries_n; it++) {
+ for (int i = 0; i < connections_n; i++) {
+ server.put((static_cast<td::int64>(i) << 24) + it);
+ }
+ for (int i = 0; i < connections_n; i++) {
+ qvalue_t value = client.get();
+ int no = value & 0x00FFFFFF;
+ int co = static_cast<int>(value >> 24);
+ // std::fprintf(stderr, "<--%d %d\n", co, no);
+ if (co < 0 || co >= connections_n || no != conn[co]++) {
+ std::fprintf(stderr, "%d %d\n", co, no);
+ std::fprintf(stderr, "expected %d %lld\n", co, static_cast<long long>(conn[co] - 1));
+ std::fprintf(stderr, "BUG\n");
+ while (true) {
+ }
+ std::exit(0);
+ }
+ }
+ }
+ // 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) override {
+ 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 : public td::Benchmark {
+ enum { QN = 504 };
+
+ struct Thread {
+ int int_id;
+ pthread_t id;
+ QueueT queue;
+ Thread *next;
+ char pad[64];
+
+ void *run() {
+ qvalue_t value;
+ // std::fprintf(stderr, "start %d\n", int_id);
+ 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() override {
+ 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() override {
+ for (int i = 0; i < QN; i++) {
+ q[i].queue.destroy();
+ }
+ }
+
+ void run(int n) override {
+ for (int i = 0; i < QN; i++) {
+ pthread_create(&q[i].id, nullptr, run_gateway, &q[i]);
+ }
+
+ std::fprintf(stderr, "run %d\n", n);
+ 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);
+ }
+ }
+};
+
+int main() {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
+#define BENCH_Q2(Q, N) \
+ std::fprintf(stderr, "!%s %d:\t", #Q, N); \
+ td::bench(QueueBenchmark2<Q>(N));
+#define BENCH_Q(Q, N) \
+ std::fprintf(stderr, "%s %d:\t", #Q, N); \
+ td::bench(QueueBenchmark<Q>(N));
+
+#define BENCH_R(Q) \
+ std::fprintf(stderr, "%s:\t", #Q); \
+ td::bench(RingBenchmark<Q>());
+ // TODO: yield makes it extremely slow. Yet some backoff may be necessary.
+ // BENCH_R(SemQueue);
+ // BENCH_R(td::PollQueue<qvalue_t>);
+
+ BENCH_Q2(td::PollQueue<qvalue_t>, 1);
+ BENCH_Q2(td::MpscPollableQueue<qvalue_t>, 1);
+ BENCH_Q2(td::PollQueue<qvalue_t>, 100);
+ BENCH_Q2(td::MpscPollableQueue<qvalue_t>, 100);
+ BENCH_Q2(td::PollQueue<qvalue_t>, 10);
+ BENCH_Q2(td::MpscPollableQueue<qvalue_t>, 10);
+
+ BENCH_Q(VarQueue, 1);
+ // BENCH_Q(FdQueue, 1);
+ // BENCH_Q(BufferedFdQueue, 1);
+ BENCH_Q(PipeQueue, 1);
+ BENCH_Q(SemCheatQueue, 1);
+ BENCH_Q(SemQueue, 1);
+
+ // BENCH_Q2(td::PollQueue<qvalue_t>, 100);
+ // BENCH_Q2(td::PollQueue<qvalue_t>, 10);
+ // BENCH_Q2(td::PollQueue<qvalue_t>, 4);
+ // BENCH_Q2(td::InfBackoffQueue<qvalue_t>, 100);
+
+ // BENCH_Q2(td::InfBackoffQueue<qvalue_t>, 1);
+ // BENCH_Q(SemCheatQueue, 1);
+
+ // BENCH_Q(BufferedFdQueue, 100);
+ // BENCH_Q(BufferedFdQueue, 10);
+
+ // BENCH_Q(BufferQueue, 4);
+ // BENCH_Q(BufferQueue, 100);
+ // BENCH_Q(BufferQueue, 10);
+ // BENCH_Q(BufferQueue, 1);
+
+ return 0;
+}
diff --git a/libs/tdlib/td/benchmark/bench_tddb.cpp b/libs/tdlib/td/benchmark/bench_tddb.cpp
new file mode 100644
index 0000000000..91e957a501
--- /dev/null
+++ b/libs/tdlib/td/benchmark/bench_tddb.cpp
@@ -0,0 +1,108 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/MessageId.h"
+#include "td/telegram/MessagesDb.h"
+#include "td/telegram/UserId.h"
+
+#include "td/utils/benchmark.h"
+#include "td/utils/buffer.h"
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/Random.h"
+#include "td/utils/Status.h"
+
+#include <memory>
+
+namespace td {
+
+static Status init_db(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 Status::OK();
+}
+
+class MessagesDbBench : public Benchmark {
+ public:
+ string get_description() const override {
+ return "MessagesDb";
+ }
+ void start_up() override {
+ LOG(ERROR) << "START UP";
+ do_start_up().ensure();
+ scheduler_->start();
+ }
+ void run(int n) override {
+ auto guard = scheduler_->get_current_guard();
+ for (int i = 0; i < n; i += 20) {
+ auto dialog_id = DialogId{UserId{Random::fast(1, 100)}};
+ auto message_id_raw = Random::fast(1, 100000);
+ for (int j = 0; j < 20; j++) {
+ auto message_id = MessageId{ServerMessageId{message_id_raw + j}};
+ auto unique_message_id = ServerMessageId{i + 1};
+ auto sender_user_id = UserId{Random::fast(1, 1000)};
+ auto random_id = i + 1;
+ auto ttl_expires_at = 0;
+ auto data = BufferSlice(Random::fast(100, 299));
+
+ // use async on same thread.
+ messages_db_async_->add_message({dialog_id, message_id}, unique_message_id, sender_user_id, random_id,
+ ttl_expires_at, 0, 0, "", std::move(data), Promise<>());
+ }
+ }
+ }
+ void tear_down() override {
+ scheduler_->run_main(0.1);
+ {
+ auto guard = scheduler_->get_current_guard();
+ sql_connection_.reset();
+ messages_db_sync_safe_.reset();
+ messages_db_async_.reset();
+ }
+
+ scheduler_->finish();
+ scheduler_.reset();
+ LOG(ERROR) << "TEAR DOWN";
+ }
+
+ private:
+ std::unique_ptr<td::ConcurrentScheduler> scheduler_;
+ std::shared_ptr<SqliteConnectionSafe> sql_connection_;
+ std::shared_ptr<MessagesDbSyncSafeInterface> messages_db_sync_safe_;
+ std::shared_ptr<MessagesDbAsyncInterface> messages_db_async_;
+
+ Status do_start_up() {
+ scheduler_ = std::make_unique<ConcurrentScheduler>();
+ scheduler_->init(1);
+
+ auto guard = scheduler_->get_current_guard();
+
+ string sql_db_name = "testdb.sqlite";
+ sql_connection_ = std::make_shared<SqliteConnectionSafe>(sql_db_name);
+ auto &db = sql_connection_->get();
+ TRY_STATUS(init_db(db));
+
+ db.exec("BEGIN TRANSACTION").ensure();
+ // version == 0 ==> db will be destroyed
+ TRY_STATUS(init_messages_db(db, 0));
+ db.exec("COMMIT TRANSACTION").ensure();
+
+ messages_db_sync_safe_ = create_messages_db_sync(sql_connection_);
+ messages_db_async_ = create_messages_db_async(messages_db_sync_safe_, 0);
+ return Status::OK();
+ }
+};
+} // namespace td
+
+int main() {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
+ bench(td::MessagesDbBench());
+ return 0;
+}
diff --git a/libs/tdlib/td/benchmark/rmdir.cpp b/libs/tdlib/td/benchmark/rmdir.cpp
new file mode 100644
index 0000000000..f1676baa63
--- /dev/null
+++ b/libs/tdlib/td/benchmark/rmdir.cpp
@@ -0,0 +1,28 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/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, bool is_dir) {
+ cnt++;
+ LOG(INFO) << path << " " << is_dir;
+ // if (is_dir) {
+ // td::rmdir(path);
+ //} else {
+ // td::unlink(path);
+ //}
+ });
+ LOG(INFO) << status << ": " << cnt;
+ return 0;
+}
diff --git a/libs/tdlib/td/benchmark/wget.cpp b/libs/tdlib/td/benchmark/wget.cpp
new file mode 100644
index 0000000000..dba997e61f
--- /dev/null
+++ b/libs/tdlib/td/benchmark/wget.cpp
@@ -0,0 +1,39 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/PromiseFuture.h"
+
+#include "td/net/HttpQuery.h"
+#include "td/net/Wget.h"
+
+#include "td/utils/logging.h"
+#include "td/utils/Status.h"
+
+#include <memory>
+#include <string>
+
+int main(int argc, char *argv[]) {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(INFO));
+ td::VERBOSITY_NAME(fd) = VERBOSITY_NAME(INFO);
+
+ std::string url = (argc > 1 ? argv[1] : "https://telegram.org");
+ auto scheduler = std::make_unique<td::ConcurrentScheduler>();
+ scheduler->init(0);
+ scheduler
+ ->create_actor_unsafe<td::Wget>(0, "Client", td::PromiseCreator::lambda([](td::Result<td::HttpQueryPtr> res) {
+ LOG(ERROR) << *res.ok();
+ td::Scheduler::instance()->finish();
+ }),
+ url)
+ .release();
+ scheduler->start();
+ while (scheduler->run_main(10)) {
+ // empty
+ }
+ scheduler->finish();
+ return 0;
+}