Skip to content

Commit 5e680f7

Browse files
Performance measurement (#532)
* Benchmark implementation. Co-authored-by: unlogisch04 <98281608+unlogisch04@users.noreply.github.com> * Make benchmark printing non-default * fix format * remove hard define * Add Guards to Benchmark * Cleanup --------- Co-authored-by: Gorbit99 <gorbitgames@gmail.com>
1 parent 0aa998c commit 5e680f7

6 files changed

Lines changed: 166 additions & 90 deletions

File tree

src/debug.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,8 @@
106106
#define USE_RUNTIME_CALIBRATION true
107107
#endif
108108

109-
#define DEBUG_MEASURE_SENSOR_TIME_TAKEN false
110-
111-
#ifndef DEBUG_MEASURE_SENSOR_TIME_TAKEN
112-
#define DEBUG_MEASURE_SENSOR_TIME_TAKEN false
109+
#ifndef DEBUG_MEASURE_TIME_TAKEN
110+
#define DEBUG_MEASURE_TIME_TAKEN false
113111
#endif
114112

115113
#ifndef USE_OTA_TIMEOUT

src/debugging/Benchmark.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include "Benchmark.h"
2+
3+
#include <limits>
4+
5+
namespace SlimeVR::Debugging {
6+
7+
Benchmark::Benchmark(const char* name)
8+
: name{name} {}
9+
10+
void Benchmark::before() {
11+
#if DEBUG_MEASURE_TIME_TAKEN
12+
currentMeasurementStartMicros = micros();
13+
#endif
14+
}
15+
16+
void Benchmark::after() {
17+
#if DEBUG_MEASURE_TIME_TAKEN
18+
auto timeTakenMicros = micros() - currentMeasurementStartMicros;
19+
20+
totalTimeTakenMicros += timeTakenMicros;
21+
minTimeTakenMicros = std::min(minTimeTakenMicros, timeTakenMicros);
22+
maxTimeTakenMicros = std::max(maxTimeTakenMicros, timeTakenMicros);
23+
measurementCount++;
24+
25+
auto timeSinceLastReport = millis() - lastReportMillis;
26+
if (timeSinceLastReport >= static_cast<uint32_t>(ReportsIntervalSeconds * 1000)) {
27+
printReport();
28+
reset();
29+
}
30+
#endif
31+
}
32+
33+
void Benchmark::printReport() {
34+
#if DEBUG_MEASURE_TIME_TAKEN
35+
if (measurementCount == 0) {
36+
return;
37+
}
38+
39+
auto timeSinceLastReport = millis() - lastReportMillis;
40+
uint64_t average = totalTimeTakenMicros / measurementCount;
41+
float timeTakenPercent = static_cast<float>(totalTimeTakenMicros) / 1000.0f
42+
/ timeSinceLastReport * 100.0f;
43+
m_Logger.info(
44+
"%-24s | "
45+
"avg: %5llu us | "
46+
"min: %5llu us | "
47+
"max: %5llu us | "
48+
"time taken: %5llu ms or %5.2f%% of %lu ms | count: %u",
49+
name,
50+
average,
51+
minTimeTakenMicros,
52+
maxTimeTakenMicros,
53+
totalTimeTakenMicros / 1000,
54+
timeTakenPercent,
55+
timeSinceLastReport,
56+
measurementCount
57+
);
58+
59+
lastReportMillis = millis();
60+
#endif
61+
}
62+
63+
void Benchmark::reset() {
64+
#if DEBUG_MEASURE_TIME_TAKEN
65+
totalTimeTakenMicros = 0;
66+
minTimeTakenMicros = std::numeric_limits<uint64_t>::max();
67+
maxTimeTakenMicros = 0;
68+
measurementCount = 0;
69+
#endif
70+
}
71+
72+
} // namespace SlimeVR::Debugging
Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
/*
22
SlimeVR Code is placed under the MIT license
3-
Copyright (c) 2025 Gorbit99 & SlimeVR Contributors
3+
Copyright (c) 2026 Gorbit99, unlogisch04 & SlimeVR Contributors
4+
45
Permission is hereby granted, free of charge, to any person obtaining a copy
56
of this software and associated documentation files (the "Software"), to deal
67
in the Software without restriction, including without limitation the rights
78
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
89
copies of the Software, and to permit persons to whom the Software is
910
furnished to do so, subject to the following conditions:
11+
1012
The above copyright notice and this permission notice shall be included in
1113
all copies or substantial portions of the Software.
14+
1215
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1316
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1417
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -20,39 +23,45 @@
2023

2124
#pragma once
2225

26+
#include <Arduino.h>
27+
28+
#include <cmath>
2329
#include <cstdint>
30+
#include <limits>
2431

25-
#include "logging/Logger.h"
32+
#include "../logging/Logger.h"
2633

2734
namespace SlimeVR::Debugging {
2835

29-
/*
30-
* Usage:
31-
*
32-
* TimeTakenMeasurer measurer{"Some event"};
33-
*
34-
* ...
35-
*
36-
* measurer.before();
37-
* thing to measure
38-
* measurer.after();
39-
*/
40-
class TimeTakenMeasurer {
36+
class Benchmark {
4137
public:
42-
explicit TimeTakenMeasurer(const char* name);
38+
Benchmark(const char* name);
39+
Benchmark(const Benchmark& other) = delete;
40+
Benchmark(Benchmark&& other) = delete;
41+
Benchmark& operator=(const Benchmark& other) = delete;
42+
Benchmark& operator=(Benchmark&& other) = delete;
43+
4344
void before();
4445
void after();
4546

4647
private:
47-
static constexpr float SecondsBetweenReports = 1.0f;
48+
static constexpr float ReportsIntervalSeconds = 10.0f;
4849

49-
const char* name;
50-
SlimeVR::Logging::Logger m_Logger = SlimeVR::Logging::Logger("TimeTaken");
50+
void printReport();
51+
void reset();
52+
53+
uint32_t lastReportMillis = millis();
54+
55+
uint64_t currentMeasurementStartMicros = 0;
5156

52-
uint64_t lastTimeTakenReportMillis = 0;
53-
uint64_t timeTakenMicros = 0;
57+
uint64_t totalTimeTakenMicros = 0;
58+
uint64_t minTimeTakenMicros = std::numeric_limits<uint64_t>::max();
59+
uint64_t maxTimeTakenMicros = 0;
60+
uint32_t measurementCount = 0;
61+
62+
const char* name;
5463

55-
uint64_t startMicros = 0;
64+
SlimeVR::Logging::Logger m_Logger = SlimeVR::Logging::Logger("Benchmark");
5665
};
5766

5867
} // namespace SlimeVR::Debugging

src/debugging/TimeTaken.cpp

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/main.cpp

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#include "Wire.h"
2828
#include "batterymonitor.h"
2929
#include "credentials.h"
30-
#include "debugging/TimeTaken.h"
30+
#include "debugging/Benchmark.h"
3131
#include "globals.h"
3232
#include "logging/Logger.h"
3333
#include "ota.h"
@@ -45,9 +45,17 @@ SlimeVR::Network::Connection networkConnection;
4545
SlimeVR::WiFiNetwork wifiNetwork;
4646
SlimeVR::WifiProvisioning wifiProvisioning;
4747

48-
#if DEBUG_MEASURE_SENSOR_TIME_TAKEN
49-
SlimeVR::Debugging::TimeTakenMeasurer sensorMeasurer{"Sensors"};
50-
#endif
48+
SlimeVR::Debugging::Benchmark tpsCounterBM{"tpsCounter.update()"};
49+
SlimeVR::Debugging::Benchmark globalTimerBM{"globalTimer.tick()"};
50+
SlimeVR::Debugging::Benchmark serialCommandsBM{"SerialCommands::update()"};
51+
SlimeVR::Debugging::Benchmark otaBM{"OTA::otaUpdate()"};
52+
SlimeVR::Debugging::Benchmark networkManagerBM{"networkManager.update()"};
53+
SlimeVR::Debugging::Benchmark sensorManagerBM{"sensorManager.update()"};
54+
SlimeVR::Debugging::Benchmark batteryBM{"battery.Loop()"};
55+
SlimeVR::Debugging::Benchmark ledManagerBM{"ledManager.update()"};
56+
SlimeVR::Debugging::Benchmark i2cScanBM{"I2CSCAN::update()"};
57+
SlimeVR::Debugging::Benchmark targetLooptimeBM{"TARGET_LOOPTIME_MICROS"};
58+
SlimeVR::Debugging::Benchmark printStateBM{"Serial printState()"};
5159

5260
int sensorToCalibrate = -1;
5361
bool blinking = false;
@@ -153,24 +161,44 @@ void setup() {
153161
}
154162

155163
void loop() {
164+
tpsCounterBM.before();
156165
tpsCounter.update();
166+
tpsCounterBM.after();
167+
168+
globalTimerBM.before();
157169
globalTimer.tick();
170+
globalTimerBM.after();
171+
172+
serialCommandsBM.before();
158173
SerialCommands::update();
174+
serialCommandsBM.after();
175+
176+
otaBM.before();
159177
OTA::otaUpdate();
178+
otaBM.after();
179+
180+
networkManagerBM.before();
160181
networkManager.update();
182+
networkManagerBM.after();
161183

162-
#if DEBUG_MEASURE_SENSOR_TIME_TAKEN
163-
sensorMeasurer.before();
164-
#endif
184+
sensorManagerBM.before();
165185
sensorManager.update();
166-
#if DEBUG_MEASURE_SENSOR_TIME_TAKEN
167-
sensorMeasurer.after();
168-
#endif
186+
sensorManagerBM.after();
169187

188+
batteryBM.before();
170189
battery.Loop();
190+
batteryBM.after();
191+
192+
ledManagerBM.before();
171193
ledManager.update();
194+
ledManagerBM.after();
195+
196+
i2cScanBM.before();
172197
I2CSCAN::update();
198+
i2cScanBM.after();
199+
173200
#ifdef TARGET_LOOPTIME_MICROS
201+
targetLooptimeBM.before();
174202
long elapsed = (micros() - loopTime);
175203
if (elapsed < TARGET_LOOPTIME_MICROS) {
176204
long sleepus = TARGET_LOOPTIME_MICROS - elapsed - 100; // µs to sleep
@@ -185,12 +213,15 @@ void loop() {
185213
}
186214
}
187215
loopTime = micros();
216+
targetLooptimeBM.after();
188217
#endif
189218
#if defined(PRINT_STATE_EVERY_MS) && PRINT_STATE_EVERY_MS > 0
219+
printStateBM.before();
190220
unsigned long now = millis();
191221
if (lastStatePrint + PRINT_STATE_EVERY_MS < now) {
192222
lastStatePrint = now;
193223
SerialCommands::printState();
194224
}
225+
printStateBM.after();
195226
#endif
196227
}

src/sensors/SensorManager.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,19 @@
2323

2424
#include "SensorManager.h"
2525

26+
#include <array>
27+
28+
#include "../debugging/Benchmark.h"
2629
#include "SensorBuilder.h"
2730

2831
namespace SlimeVR::Sensors {
2932

33+
std::array<SlimeVR::Debugging::Benchmark, 2> sensorLoopBMs{
34+
SlimeVR::Debugging::Benchmark{"IMU1 Sensor loop"},
35+
SlimeVR::Debugging::Benchmark{"IMU2 Sensor loop"}
36+
};
37+
SlimeVR::Debugging::Benchmark sensorManagerNetworkingBM{"sensorManager Network"};
38+
3039
void SensorManager::setup() {
3140
if (m_MCP.begin_I2C()) {
3241
m_Logger.info("MCP initialized");
@@ -60,7 +69,12 @@ void SensorManager::postSetup() {
6069
void SensorManager::update() {
6170
// Gather IMU data
6271
bool allIMUGood = true;
72+
size_t sensorId = 0;
6373
for (auto& sensor : m_Sensors) {
74+
if (sensorId < sensorLoopBMs.size()) {
75+
sensorLoopBMs[sensorId].before();
76+
}
77+
6478
if (sensor->isWorking()) {
6579
if (sensor->m_hwInterface != nullptr) {
6680
sensor->m_hwInterface->swapIn();
@@ -70,6 +84,11 @@ void SensorManager::update() {
7084
if (sensor->getSensorState() == SensorStatus::SENSOR_ERROR) {
7185
allIMUGood = false;
7286
}
87+
88+
if (sensorId < sensorLoopBMs.size()) {
89+
sensorLoopBMs[sensorId].after();
90+
}
91+
sensorId++;
7392
}
7493

7594
statusManager.setStatus(SlimeVR::Status::IMU_ERROR, !allIMUGood);
@@ -106,6 +125,7 @@ void SensorManager::update() {
106125
m_LastBundleSentAtMicros = now;
107126
#endif
108127

128+
sensorManagerNetworkingBM.before();
109129
#if PACKET_BUNDLING != PACKET_BUNDLING_DISABLED
110130
networkConnection.beginBundle();
111131
#endif
@@ -119,6 +139,7 @@ void SensorManager::update() {
119139
#if PACKET_BUNDLING != PACKET_BUNDLING_DISABLED
120140
networkConnection.endBundle();
121141
#endif
142+
sensorManagerNetworkingBM.after();
122143
}
123144

124145
} // namespace SlimeVR::Sensors

0 commit comments

Comments
 (0)