Railroader:
Use Serial Minitor and print out the loop execution times and show us. How frequently are the abnormalities appearin?
If you try and do that at 2 ms per loop you would soon fill the serial output buffer and choke the sketch.
What -is- possible if there is enough RAM is to collect 40 or more timestamps, the low 16 bits will do, in an array and then print the elapsed times between array values (end - start = elapsed).
When I write non-blocking code I want loop() running fast, 10's of times per milli.
To that end I break my tasks down to run short pieces only as long pieces, especially inner loops are execution-blocking =at the speeds I want loop to run.=
Literally when I process an array or button matrix or like, I process only one element per pass through loop if I can -- sometimes I can't, the data is required immediately.
Break a long process stretch up using a switch-case/state machine.
Don't use floats. AVR has no FPU. Make your working units smaller than your desired precision and integers will do just fine.
Consider Arduino time is in millis and micros(4-granular). If I want to see 2-place seconds I work in millis and have 1 place to round-off that doesn't matter. If I want 3 places, my units become micros.
Consider how in electronics we work in milliamps, not fractional amps, and use microamps when mA isn't small enough.
Use the smallest integers that can do the job -- but don't do mixed math in equations, cast the little ones up then.
If you have long calculations that you can table solutions to in PROGMEM and replace all those calculation cycles with a fetch or maybe 2 fetches and an interpolation. If you learned math on calculators you may need to look that up.
A lookup table of ints can hold sine * 10000. Using it is 100x faster than using sin(). Sine x fixed radius would save more cycles, etc. The sin() function runs a series calculation with a lot of divides, all in floats, it's a Cycle Hog.