optimize handleEvent() hot path

- Use QStringView to avoid string allocations during parsing
- Use QLatin1String for comparisons (faster than QString)
- Manual indexOf parsing instead of split('.') allocation
- Early return for non-session events

Reduces allocations in the agent.state hot path from ~7 QStrings
to 4 (only when state actually changes and needs to be stored).
This commit is contained in:
Levi Neely 2026-08-03 20:22:18 +02:00
parent 249a19e261
commit 8792539bcc
1 changed files with 54 additions and 32 deletions

View File

@ -257,48 +257,70 @@ void Ollie9pClient::stopAgentConnections()
}
// handleEvent processes a single event from eventwait.
// Event format: "topic [payload]"
// Topics: session.{sid}.{action}, session.{sid}.agent.{aid}.{action}
// Event format: "topic payload" where topic is "session.{sid}.{action}" or
// "session.{sid}.agent.{aid}.{action}"
// Optimized for the hot path: agent.state changes
void Ollie9pClient::handleEvent(const QString &eventLine)
{
if (eventLine.isEmpty()) return;
// Split into topic and payload
int spaceIdx = eventLine.indexOf(' ');
QString topic = spaceIdx > 0 ? eventLine.left(spaceIdx) : eventLine;
QString payload = spaceIdx > 0 ? eventLine.mid(spaceIdx + 1) : QString();
// Split topic and payload (avoid QString allocation where possible)
const int spaceIdx = eventLine.indexOf(QLatin1Char(' '));
const QStringView topic = spaceIdx > 0
? QStringView(eventLine).left(spaceIdx)
: QStringView(eventLine);
const QStringView payload = spaceIdx > 0
? QStringView(eventLine).mid(spaceIdx + 1)
: QStringView();
QStringList parts = topic.split('.');
// Expected: session.{sid}.* or session.{sid}.agent.{aid}.*
// Fast path: must start with "session."
if (!topic.startsWith(QLatin1String("session."))) return;
if (parts.size() >= 3 && parts[0] == "session") {
QString sessionId = parts[1];
QString action = parts[2];
// Parse: session.{sid}.{rest...}
const int firstDot = 7; // length of "session"
const int secondDot = topic.indexOf(QLatin1Char('.'), firstDot + 1);
if (secondDot < 0) return;
const QStringView sessionId = topic.mid(firstDot + 1, secondDot - firstDot - 1);
const QStringView rest = topic.mid(secondDot + 1);
// Check for agent events: "agent.{aid}.{action}"
if (rest.startsWith(QLatin1String("agent."))) {
// Parse agent.{aid}.{action}
const int agentDot = 5; // length of "agent"
const int aidEnd = rest.indexOf(QLatin1Char('.'), agentDot + 1);
if (aidEnd < 0) return;
if (action == "agent" && parts.size() >= 5) {
// Agent event: session.{sid}.agent.{aid}.{action}
QString agentId = parts[3];
QString agentAction = parts[4];
QString key = agentKey(sessionId, agentId);
const QStringView agentId = rest.mid(agentDot + 1, aidEnd - agentDot - 1);
const QStringView agentAction = rest.mid(aidEnd + 1);
if (agentAction == QLatin1String("state")) {
// Hot path: agent state change
if (payload.isEmpty()) return;
if (agentAction == "state") {
// Update cached state
if (!payload.isEmpty() && m_agentStateValues.value(key) != payload) {
m_agentStateValues[key] = payload;
emit agentStateChanged(sessionId, agentId, payload);
// Also update activeState if this is the active agent
if (sessionId == m_activeSessionId && agentId == m_agentId) {
m_activeState = payload;
emit activeStateChanged();
}
const QString sidStr = sessionId.toString();
const QString aidStr = agentId.toString();
const QString key = agentKey(sidStr, aidStr);
const QString payloadStr = payload.toString();
if (m_agentStateValues.value(key) != payloadStr) {
m_agentStateValues[key] = payloadStr;
emit agentStateChanged(sidStr, aidStr, payloadStr);
// Fast check: is this the active agent?
if (sidStr == m_activeSessionId && aidStr == m_agentId) {
m_activeState = payloadStr;
emit activeStateChanged();
}
} else if (agentAction == "new" || agentAction == "kill") {
// Structural change — need full refresh
refreshSessions();
}
} else if (action == "pause" || action == "resume" ||
action == "new" || action == "kill" || action == "rename") {
// Session-level structural change — need full refresh
} else if (agentAction == QLatin1String("new") || agentAction == QLatin1String("kill")) {
refreshSessions();
}
} else {
// Session-level event
if (rest == QLatin1String("pause") || rest == QLatin1String("resume") ||
rest == QLatin1String("new") || rest == QLatin1String("kill") ||
rest == QLatin1String("rename")) {
refreshSessions();
}
}