mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-06-08 02:17:07 +08:00
Performance counters: Estimate RMS for elapsed counters. Allow to use a perf counter across processes, deal with overruns and other resulting inconsistencies from cross-process use.
This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include <drivers/drv_hrt.h>
|
||||
#include <math.h>
|
||||
@@ -67,10 +68,13 @@ struct perf_ctr_count {
|
||||
struct perf_ctr_elapsed {
|
||||
struct perf_ctr_header hdr;
|
||||
uint64_t event_count;
|
||||
uint64_t event_overruns;
|
||||
uint64_t time_start;
|
||||
uint64_t time_total;
|
||||
uint64_t time_least;
|
||||
uint64_t time_most;
|
||||
float mean;
|
||||
float M2;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -126,6 +130,28 @@ perf_alloc(enum perf_counter_type type, const char *name)
|
||||
return ctr;
|
||||
}
|
||||
|
||||
perf_counter_t
|
||||
perf_alloc_once(enum perf_counter_type type, const char *name)
|
||||
{
|
||||
perf_counter_t handle = (perf_counter_t)sq_peek(&perf_counters);
|
||||
|
||||
while (handle != NULL) {
|
||||
if (!strcmp(handle->name, name)) {
|
||||
if (type == handle->type) {
|
||||
/* they are the same counter */
|
||||
return handle;
|
||||
} else {
|
||||
/* same name but different type, assuming this is an error and not intended */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
handle = (perf_counter_t)sq_next(&handle->link);
|
||||
}
|
||||
|
||||
/* if the execution reaches here, no existing counter of that name was found */
|
||||
return perf_alloc(type, name);
|
||||
}
|
||||
|
||||
void
|
||||
perf_free(perf_counter_t handle)
|
||||
{
|
||||
@@ -213,18 +239,30 @@ perf_end(perf_counter_t handle)
|
||||
struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle;
|
||||
|
||||
if (pce->time_start != 0) {
|
||||
hrt_abstime elapsed = hrt_absolute_time() - pce->time_start;
|
||||
int64_t elapsed = hrt_absolute_time() - pce->time_start;
|
||||
|
||||
pce->event_count++;
|
||||
pce->time_total += elapsed;
|
||||
if (elapsed < 0) {
|
||||
pce->event_overruns++;
|
||||
} else {
|
||||
|
||||
if ((pce->time_least > elapsed) || (pce->time_least == 0))
|
||||
pce->time_least = elapsed;
|
||||
pce->event_count++;
|
||||
pce->time_total += elapsed;
|
||||
|
||||
if (pce->time_most < elapsed)
|
||||
pce->time_most = elapsed;
|
||||
if ((pce->time_least > (uint64_t)elapsed) || (pce->time_least == 0))
|
||||
pce->time_least = elapsed;
|
||||
|
||||
pce->time_start = 0;
|
||||
if (pce->time_most < (uint64_t)elapsed)
|
||||
pce->time_most = elapsed;
|
||||
|
||||
// maintain mean and variance of the elapsed time in seconds
|
||||
// Knuth/Welford recursive mean and variance of update intervals (via Wikipedia)
|
||||
float dt = elapsed / 1e6f;
|
||||
float delta_intvl = dt - pce->mean;
|
||||
pce->mean += delta_intvl / pce->event_count;
|
||||
pce->M2 += delta_intvl * (dt - pce->mean);
|
||||
|
||||
pce->time_start = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -310,14 +348,17 @@ perf_print_counter_fd(int fd, perf_counter_t handle)
|
||||
|
||||
case PC_ELAPSED: {
|
||||
struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle;
|
||||
float rms = sqrtf(pce->M2 / (pce->event_count-1));
|
||||
|
||||
dprintf(fd, "%s: %llu events, %lluus elapsed, %lluus avg, min %lluus max %lluus\n",
|
||||
handle->name,
|
||||
pce->event_count,
|
||||
pce->time_total,
|
||||
pce->time_total / pce->event_count,
|
||||
pce->time_least,
|
||||
pce->time_most);
|
||||
dprintf(fd, "%s: %llu events, %llu overruns, %lluus elapsed, %lluus avg, min %lluus max %lluus %5.3fus rms\n",
|
||||
handle->name,
|
||||
pce->event_count,
|
||||
pce->event_overruns,
|
||||
pce->time_total,
|
||||
pce->time_total / pce->event_count,
|
||||
pce->time_least,
|
||||
pce->time_most,
|
||||
(double)(1e6f * rms));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -325,14 +366,13 @@ perf_print_counter_fd(int fd, perf_counter_t handle)
|
||||
struct perf_ctr_interval *pci = (struct perf_ctr_interval *)handle;
|
||||
float rms = sqrtf(pci->M2 / (pci->event_count-1));
|
||||
|
||||
dprintf(fd, "%s: %llu events, %lluus avg, min %lluus max %lluus %5.3f msec mean %5.3f msec rms\n",
|
||||
handle->name,
|
||||
pci->event_count,
|
||||
(pci->time_last - pci->time_first) / pci->event_count,
|
||||
pci->time_least,
|
||||
pci->time_most,
|
||||
(double)(1000 * pci->mean),
|
||||
(double)(1000 * rms));
|
||||
dprintf(fd, "%s: %llu events, %lluus avg, min %lluus max %lluus %5.3fus rms\n",
|
||||
handle->name,
|
||||
pci->event_count,
|
||||
(pci->time_last - pci->time_first) / pci->event_count,
|
||||
pci->time_least,
|
||||
pci->time_most,
|
||||
(double)(1e6f * rms));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ typedef struct perf_ctr_header *perf_counter_t;
|
||||
__BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Create a new counter.
|
||||
* Create a new local counter.
|
||||
*
|
||||
* @param type The type of the new counter.
|
||||
* @param name The counter name.
|
||||
@@ -65,6 +65,16 @@ __BEGIN_DECLS
|
||||
*/
|
||||
__EXPORT extern perf_counter_t perf_alloc(enum perf_counter_type type, const char *name);
|
||||
|
||||
/**
|
||||
* Get the reference to an existing counter or create a new one if it does not exist.
|
||||
*
|
||||
* @param type The type of the counter.
|
||||
* @param name The counter name.
|
||||
* @return Handle for the counter, or NULL if a counter
|
||||
* could not be allocated.
|
||||
*/
|
||||
__EXPORT extern perf_counter_t perf_alloc_once(enum perf_counter_type type, const char *name);
|
||||
|
||||
/**
|
||||
* Free a counter.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user