Skip to content

Instantly share code, notes, and snippets.

@xthexder
Last active February 25, 2026 22:48
Show Gist options
  • Select an option

  • Save xthexder/4466e4625cd70059ae632f9ca4e9e32e to your computer and use it in GitHub Desktop.

Select an option

Save xthexder/4466e4625cd70059ae632f9ca4e9e32e to your computer and use it in GitHub Desktop.
Tecs benchmark of TPS to compare with SpacetimeDB
#include "utils.hh"
#include <Tecs.hh>
#include <c_abi/Tecs.hh>
#include <chrono>
#include <cstring>
#include <future>
#include <iomanip>
#include <random>
#include <thread>
#ifdef _WIN32
#include <windows.h>
#endif
#define ENTITY_COUNT 100000
#define THREAD_COUNT 50
#define INITIAL_BALANCE 1000000
using namespace testing;
namespace benchmark {
std::atomic_bool running;
typedef int64_t Balance;
using ECS = Tecs::ECS<Balance>;
static ECS ecs;
std::atomic_size_t transactionCount;
void transferJob([[maybe_unused]] MultiTimer *workerTimer) {
std::random_device rd;
std::mt19937 gen(rd());
gen.seed((uint32_t)std::hash<std::thread::id>{}(std::this_thread::get_id()));
std::uniform_int_distribution<> randEntity(0, ENTITY_COUNT - 1);
std::uniform_int_distribution<> randAmount(1, INITIAL_BALANCE);
while (running) {
Timer t(*workerTimer);
Tecs::Entity source = randEntity(gen);
Tecs::Entity dest = randEntity(gen);
uint32_t amount = randAmount(gen);
{
auto lock = ecs.StartTransaction<Tecs::Write<Balance>>();
int64_t &sourceBalance = source.Get<Balance>(lock);
if (sourceBalance >= amount) {
int64_t &destBalance = dest.Get<Balance>(lock);
sourceBalance -= amount;
destBalance += amount;
}
}
transactionCount++;
}
}
#if THREAD_COUNT > 0
void workerThread() {
MultiTimer workerTimers[THREAD_COUNT];
std::future<void> workers[THREAD_COUNT];
for (size_t i = 0; i < THREAD_COUNT; i++) {
workerTimers[i].Reset("ScriptWorker " + std::to_string(i) + " Run");
}
for (size_t i = 0; i < THREAD_COUNT; i++) {
workers[i] = std::async(&transferJob, &workerTimers[i]);
}
for (auto &worker : workers) {
worker.wait();
}
}
#endif
int runBenchmark() {
#if __cpp_lib_atomic_wait
std::cout << "Compiled with C++20 atomic.wait()" << std::endl;
#endif
#ifdef _WIN32
// Increase thread scheduler from default of 15ms
timeBeginPeriod(1);
std::shared_ptr<UINT> timePeriodReset(new UINT(1), [](UINT *period) {
timeEndPeriod(*period);
delete period;
});
#endif
{
MultiTimer timer1("Create entities Start");
MultiTimer timer2("Create entities Run");
MultiTimer timer3("Create entities Commit");
Timer t(timer1);
auto writeLock = ecs.StartTransaction<Tecs::AddRemove>();
t = timer2;
for (size_t i = 0; i < ENTITY_COUNT; i++) {
Tecs::Entity e = writeLock.NewEntity();
e.Set<Balance>(writeLock, INITIAL_BALANCE);
}
t = timer3;
}
{
auto readLock = ecs.StartTransaction<>();
std::cout << "Running with " << readLock.Entities().size() << " Entities and " << THREAD_COUNT << " threads"
<< std::endl;
}
{
Timer t("Run threads");
running = true;
#if THREAD_COUNT > 0
auto worker = std::async(&workerThread);
#endif
std::this_thread::sleep_for(std::chrono::seconds(100));
running = false;
#if THREAD_COUNT > 0
worker.wait();
#endif
}
std::cout << "Completed " << transactionCount << " transactions in 100 seconds (" << (transactionCount / 100.0f)
<< " tps)" << std::endl;
std::cout << "Benchmark complete" << std::endl;
return 0;
}
} // namespace benchmark
int main(int /* argc */, char ** /* argv */) {
return benchmark::runBenchmark();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment