From 8cbae8bb2212aa8df8db7957ffdc22354e9cfed1 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 29 Sep 2026 21:56:38 -0500 Subject: [PATCH] test(cdclient): benchmark startup and a big inventory with and without the fdb Skipped unless DLU_FDB_BENCH_XML names a character save; prints startup time, inventory load time and resident memory (private and file-backed, on Linux) for DLU_FDB_BENCH_MODE=fdb or sqlite. Co-Authored-By: Claude Opus 5.5 --- tests/dGameTests/CDFdbBenchmark.cpp | 105 ++++++++++++++++++++++++++++ tests/dGameTests/CMakeLists.txt | 1 + 2 files changed, 106 insertions(+) create mode 100644 tests/dGameTests/CDFdbBenchmark.cpp diff --git a/tests/dGameTests/CDFdbBenchmark.cpp b/tests/dGameTests/CDFdbBenchmark.cpp new file mode 100644 index 000000000..eeb3b3f70 --- /dev/null +++ b/tests/dGameTests/CDFdbBenchmark.cpp @@ -0,0 +1,105 @@ +#include "GameDependencies.h" + +#include +#include +#include +#include +#include +#include + +#include "CDClientDatabase.h" +#include "CDClientManager.h" +#include "CDComponentsRegistryTable.h" +#include "CDFdb.h" +#include "CDItemComponentTable.h" +#include "CDObjectsTable.h" +#include "Entity.h" +#include "InventoryComponent.h" +#include "tinyxml2.h" + +#include + +// Startup and inventory load with and without the fdb. Skipped unless DLU_FDB_BENCH_XML names a +// character save (charxml) to load; run once per mode, each in its own process: +// DLU_FDB_BENCH_MODE=fdb|sqlite DLU_CLIENT_RES= DLU_CDSERVER_SQLITE= +// DLU_FDB_BENCH_XML= dGameTests --gtest_filter=CDFdbBenchmark.* +namespace { + // Resident memory in KiB: anonymous (private to the process) and file-backed (shareable) + struct Rss { + int64_t anon = -1; + int64_t file = -1; + }; + + Rss ReadRss() { + Rss rss; +#ifdef __linux__ + std::ifstream status("/proc/self/status"); + std::string key; + while (status >> key) { + if (key == "RssAnon:") status >> rss.anon; + else if (key == "RssFile:") status >> rss.file; + else status.ignore(1024, '\n'); + } +#endif + return rss; + } + + double MillisecondsSince(std::chrono::steady_clock::time_point start) { + return std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + } +} + +class CDFdbBenchmark : public GameDependenciesTest { +protected: + void TearDown() override { + if (!IsSkipped()) TearDownDependencies(); + } +}; + +TEST_F(CDFdbBenchmark, StartupAndInventoryLoad) { + const char* xmlPath = std::getenv("DLU_FDB_BENCH_XML"); + const char* sqlitePath = std::getenv("DLU_CDSERVER_SQLITE"); + if (!xmlPath || !sqlitePath) GTEST_SKIP() << "Set DLU_FDB_BENCH_XML and DLU_CDSERVER_SQLITE to run the benchmark"; + const std::string mode = std::getenv("DLU_FDB_BENCH_MODE") ? std::getenv("DLU_FDB_BENCH_MODE") : "fdb"; + std::filesystem::path fdbPath; + if (mode == "fdb") { + const char* res = std::getenv("DLU_CLIENT_RES"); + if (!res) GTEST_SKIP() << "Set DLU_CLIENT_RES for the fdb mode"; + fdbPath = std::filesystem::path(res) / "cdclient.fdb"; + } + + SetUpDependencies(); + CDClientDatabase::Connect(sqlitePath); + CDClientDatabase::ExecuteDML("PRAGMA query_only = ON;"); + + const auto before = ReadRss(); + const auto startupStart = std::chrono::steady_clock::now(); + CDClientManager::LoadValuesFromDatabase(fdbPath); + const auto startupMs = MillisecondsSince(startupStart); + const auto afterStartup = ReadRss(); + if (mode == "fdb") ASSERT_NE(CDFdb::Get(), nullptr); + + tinyxml2::XMLDocument doc; + ASSERT_EQ(doc.LoadFile(xmlPath), tinyxml2::XML_SUCCESS); + + CDClientManager::GetEntriesMutable().insert_or_assign(static_cast(info.lot), 0); + auto entity = std::make_unique(1, info); + auto* inventory = entity->AddComponent(-1); + + const auto loadStart = std::chrono::steady_clock::now(); + inventory->LoadXml(doc); + const auto loadMs = MillisecondsSince(loadStart); + const auto afterLoad = ReadRss(); + + uint32_t items = 0; + for (const auto& [type, bag] : inventory->GetInventories()) items += static_cast(bag->GetItems().size()); + + std::cout << "mode=" << mode + << " startup_ms=" << startupMs + << " inventory_load_ms=" << loadMs << " items=" << items + << " rss_anon_kib before=" << before.anon << " after_startup=" << afterStartup.anon << " after_load=" << afterLoad.anon + << " rss_file_kib before=" << before.file << " after_startup=" << afterStartup.file << " after_load=" << afterLoad.file + << std::endl; + + entity.reset(); +} diff --git a/tests/dGameTests/CMakeLists.txt b/tests/dGameTests/CMakeLists.txt index caff760c5..dca6375c0 100644 --- a/tests/dGameTests/CMakeLists.txt +++ b/tests/dGameTests/CMakeLists.txt @@ -28,6 +28,7 @@ set(DGAMETEST_SOURCES "SpiderQueenTests.cpp" "TacArcTests.cpp" "CDFdbTests.cpp" + "CDFdbBenchmark.cpp" ) add_subdirectory(dComponentsTests)