speeduino/test/timer.hpp

103 lines
2.5 KiB
C++
Raw Normal View History

Performance: optimize division (#1082) * Add udiv_32_16 * Apply udiv_32_16() where possible * Convert udiv_32_16 to assembler It's worth 20 loop/s * Remove unused functions * Remove degreesPeruSx2048 - unused * Remove angleToTime - replace with direct calls 1. Drop angleToTime() It's slow, only partially implemented and adds zero value (and has MISRA violations) 2. Consistent function naming 3. Doxygen * triggerPri_Nissan360 shouldn't set timePerDegree. It will be overwritten every loop by doCrankSpeedCalcs() * Use angleToTimeMicroSecPerDegree() instead of timePerDegree No loss in performance Increased injection open/close time accuracy (so unit test values must change) Can remove timePerDegree global. * Hide (encapsulate) crank math globals. * Base all angle to time conversions on decoder computed variables. This is within 2us of the revolution based method and is much faster - which is essentially zero percent change. * Performance: move calculation of degreesPeruSx32768 into decoders. Remove doCrankSpeedCalcs() - it's doing nothing at the moment. * Apply libdivide to triggerSetEndTeeth functions. Since triggerToothAngle is set once at initialization time, we can generate the libdivide struct once and reuse it many times. * Remove lastToothCalcAdvance - unused * Replace 16-bit division with shift * Replace 32-bit divison with 16-bit division * Avoid 32-bit division; use div100() * inline percentage() * Optimize div100() * MISRA fixes * Replace magic numbers with #defs * Replace libdivide structs with inline constants No perf or memory changes * Use fixed types for PWM max count variables * Accurate rounded integer division * Formalise rounding behavior (DIV_ROUND_CORRECT) * Apply DIV_ROUND_CORRECT to DIV_ROUND_CLOSEST(), UDIV_ROUND_CLOSEST(), div100(), div360(), percentage() & halfPercentage() * Add, fix & improve unit tests * Add udiv_32_16_closest() * Perf: Limit percentage calculations to 16-bits * MISRA fixes * Add compare_executiontime() to encapsulate common perf testing code * Signed to unsigned division * Convert ignitionLimits() to an inline function. Slight speed up, probably due to removing multiple evaluations of macro arguments. * Split unit tests up. * udiv_32_16 - check for valid parameters
2023-11-05 14:10:08 -08:00
#pragma once
#include <inttypes.h>
#if defined(__AVR__)
#include <Arduino.h>
#else
#include <sys/time.h>
#endif
#if !defined(MILLIS_PER_SEC)
#define MILLIS_PER_SEC 1000ULL
#endif
#if !defined(MICROS_PER_SEC)
#define MICROS_PER_SEC (MILLIS_PER_SEC*1000)
#endif
#if !defined(NANOS_PER_SEC)
#define NANOS_PER_SEC (MICROS_PER_SEC*1000)
#endif
class timer {
private:
#if defined(__AVR__)
uint32_t start_time;
uint32_t end_time;
#else
struct timeval start_time;
struct timeval end_time;
#endif
public:
timer() {
}
void start() {
#if defined(__AVR__)
start_time = micros();
#else
gettimeofday(&start_time, NULL);
#endif
}
void stop() {
#if defined(__AVR__)
end_time = micros();
#else
gettimeofday(&end_time, NULL);
#endif
}
uint32_t duration_micros() {
#if defined(__AVR__)
return end_time-start_time;
#else
return (uint32_t)(((end_time.tv_sec - start_time.tv_sec) * MICROS_PER_SEC) + (end_time.tv_usec - start_time.tv_usec));
#endif
}
};
template <typename TLoop, typename TParam>
void measure_executiontime(uint16_t iterations, TLoop from, TLoop to, TLoop step, timer &measure, TParam param, void (*pTestFun)(TLoop, TParam)) {
measure.start();
for (uint16_t loop=0; loop<iterations; ++loop)
{
for (TLoop a = from; a < to; a+=step)
{
pTestFun(a, param);
}
}
measure.stop();
}
template <typename TParam>
struct execution_time {
TParam result;
uint32_t durationMicros;
};
template <typename TParam>
struct comparative_execution_times {
execution_time<TParam> timeA;
execution_time<TParam> timeB;
};
template <typename TLoop, typename TParam>
comparative_execution_times<TParam> compare_executiontime(uint16_t iterations, TLoop from, TLoop to, TLoop step, void (*pTestFunA)(TLoop, TParam&), void (*pTestFunB)(TLoop, TParam&)) {
timer timerA;
TParam paramA = 0;
measure_executiontime<TLoop, TParam&>(iterations, from, to, step, timerA, paramA, pTestFunA);
timer timerB;
TParam paramB = 0;
measure_executiontime<TLoop, TParam&>(iterations, from, to, step, timerB, paramB, pTestFunB);
char buffer[128];
sprintf(buffer, "Timing: %" PRIu32 ", %" PRIu32, timerA.duration_micros(), timerB.duration_micros());
TEST_MESSAGE(buffer);
return comparative_execution_times<TParam> {
.timeA = execution_time<TParam> { .result = paramA, .durationMicros = timerA.duration_micros()},
.timeB = execution_time<TParam> { .result = paramB, .durationMicros = timerB.duration_micros()}
};
}