mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-15 17:14:07 +00:00
feat(servers): time the main loops' phases and the known heavy spots
Frames and phase scopes in the world, auth, chat and UGC loops (packets per type, entities, physics, ghosting, replica, spawners, log flush, saves, web requests by route). Scopes at LoadPlayer, CreateEntity, each component's construction, InventoryComponent::LoadXml, script timers and a world's zone load; game database queries in the query helpers; CDClient statements timed through sqlite3_trace_v2 as CDClient <table> (CppSQLite3DB gets a handle accessor). Task 96. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
#include "CDClientDatabase.h"
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "Profiler.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// Static Variables
|
||||
static CppSQLite3DB* conn = new CppSQLite3DB();
|
||||
@@ -7,10 +13,58 @@ static CppSQLite3DB* conn = new CppSQLite3DB();
|
||||
// Status Variables
|
||||
bool CDClientDatabase::isConnected = false;
|
||||
|
||||
namespace {
|
||||
// Frame timing (Profiler.h): each CDClient statement the main thread runs, timed from its first step to its end,
|
||||
// counted under the scope that ran it as "CDClient <table>" (the first table after FROM)
|
||||
std::vector<std::pair<sqlite3_stmt*, int64_t>> g_Running;
|
||||
|
||||
const char* TableScope(const char* sql) {
|
||||
if (!sql) return "CDClient";
|
||||
const std::string_view text(sql);
|
||||
for (size_t i = 0; i + 5 < text.size(); i++) {
|
||||
const bool from = (text[i] == 'F' || text[i] == 'f') && (text[i + 1] == 'R' || text[i + 1] == 'r') && (text[i + 2] == 'O' || text[i + 2] == 'o') &&
|
||||
(text[i + 3] == 'M' || text[i + 3] == 'm') && std::isspace(static_cast<unsigned char>(text[i + 4])) && (i == 0 || std::isspace(static_cast<unsigned char>(text[i - 1])));
|
||||
if (!from) continue;
|
||||
size_t start = i + 5;
|
||||
while (start < text.size() && std::isspace(static_cast<unsigned char>(text[start]))) start++;
|
||||
size_t end = start;
|
||||
while (end < text.size() && (std::isalnum(static_cast<unsigned char>(text[end])) || text[end] == '_')) end++;
|
||||
if (end > start) return Profiler::Intern("CDClient " + std::string(text.substr(start, end - start)));
|
||||
break;
|
||||
}
|
||||
return "CDClient";
|
||||
}
|
||||
|
||||
int Trace(unsigned type, void*, void* p, void* x) {
|
||||
if (!Profiler::IsMainThread()) return 0;
|
||||
auto* statement = static_cast<sqlite3_stmt*>(p);
|
||||
const auto now = Profiler::NowNs();
|
||||
if (type == SQLITE_TRACE_STMT) {
|
||||
for (auto& [running, start] : g_Running) {
|
||||
if (running == statement) { start = now; return 0; }
|
||||
}
|
||||
if (g_Running.size() < 64) g_Running.emplace_back(statement, now);
|
||||
return 0;
|
||||
}
|
||||
if (type != SQLITE_TRACE_PROFILE) return 0;
|
||||
// SQLite's own time as a fallback (coarse on some platforms)
|
||||
int64_t duration = x ? static_cast<int64_t>(*static_cast<sqlite3_int64*>(x)) : 0;
|
||||
for (size_t i = 0; i < g_Running.size(); i++) {
|
||||
if (g_Running[i].first != statement) continue;
|
||||
duration = now - g_Running[i].second;
|
||||
g_Running.erase(g_Running.begin() + static_cast<std::ptrdiff_t>(i));
|
||||
break;
|
||||
}
|
||||
Profiler::Local().Record(TableScope(sqlite3_sql(statement)), 0, duration, Profiler::Phase::CDCLIENT, now);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//! Opens a connection with the CDClient
|
||||
void CDClientDatabase::Connect(const std::string& filename) {
|
||||
conn->open(filename.c_str());
|
||||
isConnected = true;
|
||||
sqlite3_trace_v2(conn->handle(), SQLITE_TRACE_STMT | SQLITE_TRACE_PROFILE, Trace, nullptr);
|
||||
}
|
||||
|
||||
//! Queries the CDClient
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef __MYSQLDATABASE__H__
|
||||
#define __MYSQLDATABASE__H__
|
||||
|
||||
#include "Profiler.h"
|
||||
#include <conncpp.hpp>
|
||||
#include <memory>
|
||||
|
||||
@@ -518,6 +519,7 @@ private:
|
||||
// The return type is a PreparedStmtResultSet which keeps the PreparedStatement alive alongside the ResultSet.
|
||||
template<typename... Args>
|
||||
inline PreparedStmtResultSet ExecuteSelect(const std::string& query, Args&&... args) {
|
||||
Profiler::Scope profile("Database query", Profiler::Phase::DATABASE);
|
||||
PreparedStmtResultSet toReturn;
|
||||
toReturn.m_stmt.reset(CreatePreppedStmt(query));
|
||||
SetParams(toReturn.m_stmt, std::forward<Args>(args)...);
|
||||
@@ -528,6 +530,7 @@ private:
|
||||
|
||||
template<typename... Args>
|
||||
inline void ExecuteDelete(const std::string& query, Args&&... args) {
|
||||
Profiler::Scope profile("Database query", Profiler::Phase::DATABASE);
|
||||
std::unique_ptr<sql::PreparedStatement> preppedStmt(CreatePreppedStmt(query));
|
||||
SetParams(preppedStmt, std::forward<Args>(args)...);
|
||||
DLU_SQL_TRY_CATCH_RETHROW(preppedStmt->execute());
|
||||
@@ -535,6 +538,7 @@ private:
|
||||
|
||||
template<typename... Args>
|
||||
inline int32_t ExecuteUpdate(const std::string& query, Args&&... args) {
|
||||
Profiler::Scope profile("Database query", Profiler::Phase::DATABASE);
|
||||
std::unique_ptr<sql::PreparedStatement> preppedStmt(CreatePreppedStmt(query));
|
||||
SetParams(preppedStmt, std::forward<Args>(args)...);
|
||||
DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt->executeUpdate());
|
||||
@@ -542,6 +546,7 @@ private:
|
||||
|
||||
template<typename... Args>
|
||||
inline bool ExecuteInsert(const std::string& query, Args&&... args) {
|
||||
Profiler::Scope profile("Database query", Profiler::Phase::DATABASE);
|
||||
std::unique_ptr<sql::PreparedStatement> preppedStmt(CreatePreppedStmt(query));
|
||||
SetParams(preppedStmt, std::forward<Args>(args)...);
|
||||
DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt->execute());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef SQLITEDATABASE_H
|
||||
#define SQLITEDATABASE_H
|
||||
|
||||
#include "Profiler.h"
|
||||
#include "CppSQLite3.h"
|
||||
|
||||
#include "GameDatabase.h"
|
||||
@@ -502,6 +503,7 @@ private:
|
||||
// The return type is a unique_ptr to the result set, which is deleted automatically when it goes out of scope
|
||||
template<typename... Args>
|
||||
inline std::pair<CppSQLite3Statement, CppSQLite3Query> ExecuteSelect(const std::string& query, Args&&... args) {
|
||||
Profiler::Scope profile("Database query", Profiler::Phase::DATABASE);
|
||||
std::pair<CppSQLite3Statement, CppSQLite3Query> toReturn;
|
||||
toReturn.first = CreatePreppedStmt(query);
|
||||
SetParams(toReturn.first, std::forward<Args>(args)...);
|
||||
@@ -511,6 +513,7 @@ private:
|
||||
|
||||
template<typename... Args>
|
||||
inline void ExecuteDelete(const std::string& query, Args&&... args) {
|
||||
Profiler::Scope profile("Database query", Profiler::Phase::DATABASE);
|
||||
auto preppedStmt = CreatePreppedStmt(query);
|
||||
SetParams(preppedStmt, std::forward<Args>(args)...);
|
||||
DLU_SQL_TRY_CATCH_RETHROW(preppedStmt.execDML());
|
||||
@@ -518,6 +521,7 @@ private:
|
||||
|
||||
template<typename... Args>
|
||||
inline int32_t ExecuteUpdate(const std::string& query, Args&&... args) {
|
||||
Profiler::Scope profile("Database query", Profiler::Phase::DATABASE);
|
||||
auto preppedStmt = CreatePreppedStmt(query);
|
||||
SetParams(preppedStmt, std::forward<Args>(args)...);
|
||||
DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt.execDML());
|
||||
@@ -525,6 +529,7 @@ private:
|
||||
|
||||
template<typename... Args>
|
||||
inline int ExecuteInsert(const std::string& query, Args&&... args) {
|
||||
Profiler::Scope profile("Database query", Profiler::Phase::DATABASE);
|
||||
auto preppedStmt = CreatePreppedStmt(query);
|
||||
SetParams(preppedStmt, std::forward<Args>(args)...);
|
||||
DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt.execDML());
|
||||
|
||||
Reference in New Issue
Block a user