/* SPDX-License-Identifier: GPL-3.0-or-later * Copyright © 2026 The TokTok team. */ #include #include #include "../../testing/support/public/simulation.hh" #include "../../toxcore/network.h" #include "../../toxcore/tox.h" namespace { using tox::test::Simulation; struct Context { size_t count = 0; }; void BM_ToxMessengerThroughput(benchmark::State &state) { Simulation sim; sim.net().set_latency(5); auto node1 = sim.create_node(); auto node2 = sim.create_node(); auto opts1 = std::unique_ptr( tox_options_new(nullptr), tox_options_free); tox_options_set_log_user_data(opts1.get(), const_cast("Tox1")); tox_options_set_ipv6_enabled(opts1.get(), false); tox_options_set_local_discovery_enabled(opts1.get(), false); auto opts2 = std::unique_ptr( tox_options_new(nullptr), tox_options_free); tox_options_set_log_user_data(opts2.get(), const_cast("Tox2")); tox_options_set_ipv6_enabled(opts2.get(), false); tox_options_set_local_discovery_enabled(opts2.get(), false); auto tox1 = node1->create_tox(opts1.get()); auto tox2 = node2->create_tox(opts2.get()); if (!tox1 || !tox2) { state.SkipWithError("Failed to create Tox instances"); return; } uint8_t tox1_pk[TOX_PUBLIC_KEY_SIZE]; tox_self_get_public_key(tox1.get(), tox1_pk); uint8_t tox2_pk[TOX_PUBLIC_KEY_SIZE]; tox_self_get_public_key(tox2.get(), tox2_pk); uint8_t tox1_dht_id[TOX_PUBLIC_KEY_SIZE]; tox_self_get_dht_id(tox1.get(), tox1_dht_id); uint8_t tox2_dht_id[TOX_PUBLIC_KEY_SIZE]; tox_self_get_dht_id(tox2.get(), tox2_dht_id); Tox_Err_Friend_Add friend_add_err; uint32_t f1 = tox_friend_add_norequest(tox1.get(), tox2_pk, &friend_add_err); uint32_t f2 = tox_friend_add_norequest(tox2.get(), tox1_pk, &friend_add_err); uint16_t port1 = node1->get_primary_socket()->local_port(); uint16_t port2 = node2->get_primary_socket()->local_port(); char ip1[TOX_INET6_ADDRSTRLEN]; ip_parse_addr(&node1->ip, ip1, sizeof(ip1)); char ip2[TOX_INET6_ADDRSTRLEN]; ip_parse_addr(&node2->ip, ip2, sizeof(ip2)); tox_bootstrap(tox2.get(), ip1, port1, tox1_dht_id, nullptr); tox_bootstrap(tox1.get(), ip2, port2, tox2_dht_id, nullptr); bool connected = false; sim.run_until( [&]() { tox_iterate(tox1.get(), nullptr); tox_iterate(tox2.get(), nullptr); sim.advance_time(90); // +10ms from run_until = 100ms connected = (tox_friend_get_connection_status(tox1.get(), f1, nullptr) != TOX_CONNECTION_NONE && tox_friend_get_connection_status(tox2.get(), f2, nullptr) != TOX_CONNECTION_NONE); return connected; }, 60000); if (!connected) { state.SkipWithError("Failed to connect toxes within 60s"); return; } const uint8_t msg[] = "benchmark message"; const size_t msg_len = sizeof(msg); Context ctx; tox_callback_friend_message(tox2.get(), [](Tox *, uint32_t, Tox_Message_Type, const uint8_t *, size_t, void *user_data) { static_cast(user_data)->count++; }); for (auto _ : state) { tox_friend_send_message(tox1.get(), f1, TOX_MESSAGE_TYPE_NORMAL, msg, msg_len, nullptr); for (int i = 0; i < 5; ++i) { sim.advance_time(1); tox_iterate(tox1.get(), nullptr); tox_iterate(tox2.get(), &ctx); } } state.counters["messages_received"] = benchmark::Counter(static_cast(ctx.count), benchmark::Counter::kAvgThreads); } BENCHMARK(BM_ToxMessengerThroughput); void BM_ToxMessengerBidirectional(benchmark::State &state) { Simulation sim; sim.net().set_latency(5); auto node1 = sim.create_node(); auto node2 = sim.create_node(); auto opts1 = std::unique_ptr( tox_options_new(nullptr), tox_options_free); tox_options_set_log_user_data(opts1.get(), const_cast("Tox1")); tox_options_set_ipv6_enabled(opts1.get(), false); tox_options_set_local_discovery_enabled(opts1.get(), false); auto opts2 = std::unique_ptr( tox_options_new(nullptr), tox_options_free); tox_options_set_log_user_data(opts2.get(), const_cast("Tox2")); tox_options_set_ipv6_enabled(opts2.get(), false); tox_options_set_local_discovery_enabled(opts2.get(), false); auto tox1 = node1->create_tox(opts1.get()); auto tox2 = node2->create_tox(opts2.get()); if (!tox1 || !tox2) { state.SkipWithError("Failed to create Tox instances"); return; } uint8_t tox1_pk[TOX_PUBLIC_KEY_SIZE]; tox_self_get_public_key(tox1.get(), tox1_pk); uint8_t tox2_pk[TOX_PUBLIC_KEY_SIZE]; tox_self_get_public_key(tox2.get(), tox2_pk); uint8_t tox1_dht_id[TOX_PUBLIC_KEY_SIZE]; tox_self_get_dht_id(tox1.get(), tox1_dht_id); uint8_t tox2_dht_id[TOX_PUBLIC_KEY_SIZE]; tox_self_get_dht_id(tox2.get(), tox2_dht_id); Tox_Err_Friend_Add friend_add_err; uint32_t f1 = tox_friend_add_norequest(tox1.get(), tox2_pk, &friend_add_err); uint32_t f2 = tox_friend_add_norequest(tox2.get(), tox1_pk, &friend_add_err); uint16_t port1 = node1->get_primary_socket()->local_port(); uint16_t port2 = node2->get_primary_socket()->local_port(); char ip1[TOX_INET6_ADDRSTRLEN]; ip_parse_addr(&node1->ip, ip1, sizeof(ip1)); char ip2[TOX_INET6_ADDRSTRLEN]; ip_parse_addr(&node2->ip, ip2, sizeof(ip2)); tox_bootstrap(tox2.get(), ip1, port1, tox1_dht_id, nullptr); tox_bootstrap(tox1.get(), ip2, port2, tox2_dht_id, nullptr); bool connected = false; sim.run_until( [&]() { tox_iterate(tox1.get(), nullptr); tox_iterate(tox2.get(), nullptr); sim.advance_time(90); // +10ms from run_until = 100ms connected = (tox_friend_get_connection_status(tox1.get(), f1, nullptr) != TOX_CONNECTION_NONE && tox_friend_get_connection_status(tox2.get(), f2, nullptr) != TOX_CONNECTION_NONE); return connected; }, 60000); if (!connected) { state.SkipWithError("Failed to connect toxes within 60s"); return; } const uint8_t msg[] = "benchmark message"; const size_t msg_len = sizeof(msg); Context ctx1, ctx2; tox_callback_friend_message(tox1.get(), [](Tox *, uint32_t, Tox_Message_Type, const uint8_t *, size_t, void *user_data) { static_cast(user_data)->count++; }); tox_callback_friend_message(tox2.get(), [](Tox *, uint32_t, Tox_Message_Type, const uint8_t *, size_t, void *user_data) { static_cast(user_data)->count++; }); for (auto _ : state) { tox_friend_send_message(tox1.get(), f1, TOX_MESSAGE_TYPE_NORMAL, msg, msg_len, nullptr); tox_friend_send_message(tox2.get(), f2, TOX_MESSAGE_TYPE_NORMAL, msg, msg_len, nullptr); for (int i = 0; i < 5; ++i) { sim.advance_time(1); tox_iterate(tox1.get(), &ctx1); tox_iterate(tox2.get(), &ctx2); } } state.counters["messages_received"] = benchmark::Counter( static_cast(ctx1.count + ctx2.count), benchmark::Counter::kAvgThreads); } BENCHMARK(BM_ToxMessengerBidirectional); } // namespace BENCHMARK_MAIN();