Skip to content

Commit 5defca5

Browse files
Merge pull request #71 from CU-Robotics/feature-profiler
Feature profiler
2 parents a8e0825 + 8a41f54 commit 5defca5

File tree

3 files changed

+124
-0
lines changed

3 files changed

+124
-0
lines changed

src/main.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "git_info.h"
44

55
#include "utils/timing.hpp"
6+
#include "utils/profiler.hpp"
67
#include "comms/rm_can.hpp"
78
#include "sensors/dr16.hpp"
89
#include "controls/estimator_manager.hpp"
@@ -34,6 +35,8 @@ ACS712 current_sensor;
3435
ConfigLayer config_layer;
3536
Config config;
3637

38+
Profiler prof;
39+
3740
Timer loop_timer;
3841
Timer stall_timer;
3942
Timer control_input_timer;

src/utils/profiler.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include "profiler.hpp"
2+
3+
void Profiler::begin(const char* name) {
4+
#ifdef PROFILE
5+
// search for a section by name or an empty slot
6+
for (uint32_t i = 0; i < PROF_MAX_SECTIONS; i++) {
7+
if (strncmp(sections[i].name, name, PROF_MAX_NAME) == 0 || (sections[i].count == 0 && sections[i].overflow == 0)) {
8+
// start this section
9+
strncpy(sections[i].name, name, PROF_MAX_NAME);
10+
sections[i].name[PROF_MAX_NAME] = '\0'; // ensure null termination
11+
if (sections[i].count < PROF_MAX_TIMES) {
12+
sections[i].start_times[sections[i].count] = micros();
13+
sections[i].started = 1; // set this section as "started"
14+
}
15+
return;
16+
}
17+
}
18+
#endif
19+
}
20+
21+
void Profiler::end(const char* name) {
22+
#ifdef PROFILE
23+
// find the section by name and add an end time
24+
for (uint32_t i = 0; i < PROF_MAX_SECTIONS; i++) {
25+
if (strncmp(sections[i].name, name, PROF_MAX_NAME) == 0) {
26+
uint32_t count = sections[i].count;
27+
if (count < PROF_MAX_TIMES) {
28+
sections[i].end_times[count] = micros();
29+
sections[i].count++;
30+
sections[i].started = 0; // set this section as "finished"
31+
// if this value just overflowed, or it has reached the max time count,
32+
// mark it as overflowed and reset to 0
33+
if (sections[i].count == 0 || sections[i].count == PROF_MAX_TIMES) {
34+
sections[i].overflow = 1;
35+
sections[i].count = 0;
36+
}
37+
}
38+
return;
39+
}
40+
}
41+
#endif
42+
}
43+
44+
void Profiler::print(const char* name) {
45+
#ifdef PROFILE
46+
uint32_t min = UINT32_MAX;
47+
uint32_t max = 0;
48+
uint32_t sum = 0;
49+
50+
for (uint32_t i = 0; i < PROF_MAX_SECTIONS; i++) {
51+
if (strncmp(sections[i].name, name, PROF_MAX_NAME) != 0) continue;
52+
// find actual count since this value can overflow/max out
53+
uint32_t actual_count = sections[i].overflow ? PROF_MAX_TIMES : sections[i].count;
54+
for (unsigned int j = 0; j < actual_count; j++) {
55+
// if end() was not called when we decide to print, ignore the "bad" section reading
56+
if (sections[i].started && j == sections[i].count) continue;
57+
58+
uint32_t delta = sections[i].end_times[j] - sections[i].start_times[j];
59+
// sum all deltas to be averaged upon printing
60+
sum += delta;
61+
if (delta < min) min = delta;
62+
if (delta > max) max = delta;
63+
}
64+
// print stats
65+
Serial.printf("Profiling for: %s\n Min: %u us\n Max: %u us\n Avg: %u us\n",
66+
name, min, max, sum / actual_count);
67+
return;
68+
}
69+
#endif
70+
}

src/utils/profiler.hpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#ifndef PROFILER_H
2+
#define PROFILER_H
3+
4+
// Use this flag to toggle profiling globally.
5+
#define PROFILE
6+
7+
#include <Arduino.h>
8+
9+
#define PROF_MAX_SECTIONS 4 // max number of active profiling sections
10+
#define PROF_MAX_NAME 16 // max length of section name
11+
#define PROF_MAX_TIMES (1 << 20) // max number of start/end times per section
12+
13+
/// @brief Object for profiling sections of code.
14+
struct Profiler {
15+
/// @brief Data structure for a profiling section.
16+
struct profiler_section_t {
17+
/// @brief Start time for each profiling section.
18+
uint32_t start_times[PROF_MAX_TIMES] = { 0 };
19+
/// @brief End time for each profiling section.
20+
uint32_t end_times[PROF_MAX_TIMES] = { 0 };
21+
/// @brief Number of start/end times recorded (how many deltas can be calculated).
22+
uint16_t count = 0;
23+
/// @brief Flag on whether count has overflowed or not
24+
uint8_t overflow : 7;
25+
/// @brief Flag on whether this section had begin() called on it
26+
uint8_t started : 1;
27+
/// @brief A unique name to identify the section.
28+
char name[PROF_MAX_NAME + 1] = { 0 }; // extra for null terminator
29+
};
30+
31+
// dont allocate this memory if we dont need it
32+
#ifdef PROFILE
33+
/// @brief Array of profiling sections.
34+
profiler_section_t sections[PROF_MAX_SECTIONS] = {};
35+
#endif
36+
/// @brief Start a profiling section.
37+
/// @param name A unique name to identify the section.
38+
void begin(const char* name);
39+
40+
/// @brief End a profiling section.
41+
/// @param name The name of the section to end.
42+
void end(const char* name);
43+
44+
/// @brief Print stats for a particular profiling section.
45+
/// @param name The name of the section to print statistics for.
46+
void print(const char* name);
47+
};
48+
49+
extern PROGMEM Profiler prof; // Global profiler
50+
51+
#endif // PROFILER_H

0 commit comments

Comments
 (0)