diff options
Diffstat (limited to 'libs/tdlib/td/benchmark')
-rw-r--r-- | libs/tdlib/td/benchmark/CMakeLists.txt | 59 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_actor.cpp | 290 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_crypto.cpp | 213 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_db.cpp | 237 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_empty.cpp | 9 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_handshake.cpp | 76 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_http.cpp | 78 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_http_reader.cpp | 118 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_http_server.cpp | 89 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_http_server_cheat.cpp | 138 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_http_server_fast.cpp | 121 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_log.cpp | 163 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_misc.cpp | 392 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_queue.cpp | 943 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/bench_tddb.cpp | 108 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/rmdir.cpp | 28 | ||||
-rw-r--r-- | libs/tdlib/td/benchmark/wget.cpp | 39 |
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 ∑ + + 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; +} |