The Fast Fourier Transform (FFT) is an efficient algorithm for computing the Discrete Fourier Transform (DFT): it converts a signal from the time domain into the frequency domain, revealing which frequencies are present in a signal and how strong they are. It is the workhorse behind spectrum analyzers, audio effects, filtering, and countless other DSP applications.
I recently published a new FFT library that supports both integer and floating-point data types for its calculations. Using this library, I benchmarked DSP performance across a wide range of microcontroller architectures — from a vintage 8-bit AVR all the way up to a 480 MHz ARM Cortex-M7. The results reveal some surprising winners and a few important lessons about choosing the right chip (and the right data type) for your signal-processing project.
Methodology
Each benchmark measures the time to compute a single N=64 complex FFT and IFFT for five data types: int8_t, int16_t, int32_t, float (32-bit), and double (64-bit). All timings are averaged over repeated runs and reported in microseconds (µs); throughput is derived as 1,000,000 / latency.
Executive Summary & Key Takeaways
- The single-precision float king: The STM32H7 dominates overall thanks to its 480 MHz clock and a hardware FPU with double-precision support. But the ESP32 and ESP32-S3 punch far above their weight class in single-precision
float, outperforming everything except the STM32H7. - ESP32-S3 vs. ESP32 — a real generation leap: The ESP32-S3 (Xtensa LX7) delivers a substantial upgrade over the base ESP32 (Xtensa LX6). It cuts the floating-point FFT from 75.14 µs down to 62.62 µs (~17% faster) and crushes integer IFFTs, running
int16_tin just 107.96 µs (~33% faster). - The double-precision cliff: Devices without a double-precision hardware FPU (ESP32/S3, STM32F411, UNO R4, RP2040) suffer a massive slowdown — frequently 5× to 11× — when stepping from 32-bit
floatto 64-bitdouble, because double-precision math must be emulated in software. - RP2040 integer power: The RP2040‘s integer performance is exceptionally strong — it even beats the STM32F411 in 8-bit and 16-bit FFTs — but it falls behind on floats because it has no hardware FPU and relies on its (admittedly well-optimized) ROM floating-point routines.
FFT Master Comparison Tables
FFT Calculation Latency (lower is better)
Values are in microseconds (µs) per single N=64 FFT calculation. The fastest data type per board is highlighted in bold.
| Microcontroller | Core Architecture | Clock Speed | int8_t | int16_t | int32_t | float (32-bit) | double (64-bit) |
|---|---|---|---|---|---|---|---|
| Arduino Nano | AVR ATMega328P (8-bit) | 16 MHz | 4,144.62 | 9,798.18 | 29,160.16 | 17,165.54 | N/A |
| UNO R4 | ARM Cortex-M4 (32-bit) | 48 MHz | 732.87 | 668.05 | 1,108.19 | 360.89 | 4,045.90 |
| STM32F411 | ARM Cortex-M4 (32-bit) | 100 MHz | 318.39 | 284.30 | 356.52 | 151.90 | 1,737.21 |
| RP2040 | ARM Cortex-M0+ (32-bit) | 133 MHz | 228.24 | 228.37 | 472.07 | 939.43 | 1,672.06 |
| ESP32 | Tensilica Xtensa LX6 (32-bit) | 240 MHz | 255.05 | 250.49 | 398.47 | 75.14 | 756.14 |
| ESP32-S3 | Tensilica Xtensa LX7 (32-bit) | 240 MHz | 199.44 | 190.14 | 361.17 | 62.62 | 702.66 |
| STM32H7 | ARM Cortex-M7 (32-bit) | 480 MHz | 44.76 | 36.85 | 45.81 | 23.36 | 238.10 |
FFT Throughput (higher is better)
Values represent total FFT operations computed per second (FFT/s).
| Microcontroller | int8_t | int16_t | int32_t | float | double |
|---|---|---|---|---|---|
| Arduino Nano | 241 | 102 | 34 | 58 | N/A |
| UNO R4 | 1,364 | 1,497 | 902 | 2,771 | 247 |
| STM32F411 | 3,141 | 3,517 | 2,805 | 6,583 | 576 |
| RP2040 | 4,381 | 4,379 | 2,118 | 1,064 | 598 |
| ESP32 | 3,921 | 3,992 | 2,510 | 13,308 | 1,323 |
| ESP32-S3 | 5,014 | 5,259 | 2,769 | 15,971 | 1,423 |
| STM32H7 | 22,341 | 27,141 | 21,827 | 42,808 | 4,200 |
IFFT Master Comparison Tables
IFFT Calculation Latency (lower is better)
Values are in microseconds (µs) per single N=64 Inverse FFT calculation.
| Microcontroller | Core Architecture | Clock Speed | int8_t | int16_t | int32_t | float (32-bit) | double (64-bit) |
|---|---|---|---|---|---|---|---|
| Arduino Nano | AVR ATMega328P (8-bit) | 16 MHz | 4,033.66 | 9,302.30 | 26,380.36 | 18,900.00 | N/A |
| UNO R4 | ARM Cortex-M4 (32-bit) | 48 MHz | 741.59 | 652.91 | 1,084.73 | 384.70 | 4,415.17 |
| STM32F411 | ARM Cortex-M4 (32-bit) | 100 MHz | 312.65 | 280.38 | 336.16 | 163.91 | 1,878.80 |
| RP2040 | ARM Cortex-M0+ (32-bit) | 133 MHz | 219.27 | 224.29 | 464.54 | 967.96 | 1,808.77 |
| ESP32 | Tensilica Xtensa LX6 (32-bit) | 240 MHz | 170.51 | 161.18 | 195.37 | 83.29 | 830.59 |
| ESP32-S3 | Tensilica Xtensa LX7 (32-bit) | 240 MHz | 122.90 | 107.96 | 153.10 | 69.11 | 776.86 |
| STM32H7 | ARM Cortex-M7 (32-bit) | 480 MHz | 43.65 | 36.79 | 39.06 | 24.32 | 258.70 |
IFFT Throughput (higher is better)
Values represent total IFFT operations computed per second (IFFT/s), derived directly from the latencies above (1,000,000 / latency).
| Microcontroller | int8_t | int16_t | int32_t | float | double |
|---|---|---|---|---|---|
| Arduino Nano | 247 | 107 | 37 | 52 | N/A |
| UNO R4 | 1,348 | 1,531 | 921 | 2,599 | 226 |
| STM32F411 | 3,198 | 3,566 | 2,974 | 6,100 | 532 |
| RP2040 | 4,560 | 4,458 | 2,152 | 1,033 | 552 |
| ESP32 | 5,864 | 6,204 | 5,118 | 12,006 | 1,203 |
| ESP32-S3 | 8,137 | 9,263 | 6,532 | 14,470 | 1,287 |
| STM32H7 | 22,910 | 27,181 | 25,601 | 41,118 | 3,865 |
Device-by-Device Analysis
🔴 Arduino Nano (AVR ATMega328P @ 16 MHz)
The legacy 8-bit architecture struggles immensely with DSP workloads.
- Why it’s slow: It has no FPU and no hardware multiplier for larger integers — every 32-bit multiplication must be emulated in software with dozens of assembly instructions.
- Data-type anomaly: Curiously,
float(17,165 µs) is actually faster thanint32_t(29,160 µs). The 32-bit float library benefits from 24-bit mantissa optimizations, while full 32-bit integer multiplication and scaling on an 8-bit core requires extensive software-based register shuffling. - Practical verdict: Stick to
int8_t— or better yet, stick to a different chip for anything beyond toy FFT sizes.
🔵 UNO R4 (Renesas RA4M1 Cortex-M4 @ 48 MHz)
A major modernization of the classic Arduino form factor.
- FPU power: The Cortex-M4’s single-precision FPU makes
floatmath remarkably quick (360.89 µs) — nearly 11× faster than its software-emulateddoublemath (4,045.90 µs). - Its modest 48 MHz clock keeps it behind the other 32-bit boards, but for an entry-level Arduino it is a huge step up from the Nano.
🟣 RP2040 (Cortex-M0+ @ 133 MHz)
The Raspberry Pi silicon lacks a hardware FPU but offers remarkably fast integer execution.
- Integer strength: It beats the STM32F411 at
int8_tandint16_tprocessing, helped by its higher clock speed (133 MHz vs. 100 MHz). - Smart floating-point ROM: Without an FPU, the RP2040 falls back on optimized floating-point routines burned directly into its boot ROM. This keeps its software
float(939 µs) anddouble(1,672 µs) performance respectable — though still far behind hardware-accelerated chips. - Practical verdict: This is the one modern board where an integer FFT is clearly the right choice.
🟢 STM32F411 (Cortex-M4 @ 100 MHz)
A well-balanced DSP workhorse.
- Equipped with ARM’s DSP instruction extensions and a single-precision FPU, it shows exceptionally uniform integer scaling and rapid
floatspeeds (151.90 µs) — roughly 6,500 FFTs per second.
🟠 ESP32 (Xtensa LX6 @ 240 MHz)
A long-time favorite for consumer audio projects — and the benchmark shows why.
- Single-precision optimization: Thanks to its high 240 MHz clock and an efficient single-precision pipeline, its
floatFFT is blazingly fast at 75.14 µs (over 13,300 FFTs per second) — beating everything below the STM32H7 by a wide margin. - The IFFT speedup: Interestingly, the ESP32 computes integer IFFTs significantly faster than forward FFTs (e.g., the
int32_tIFFT takes 195.37 µs vs. 398.47 µs for the FFT), suggesting highly efficient compiler code paths for the backward-scaling butterfly passes of the inverse algorithm.
⚡ ESP32-S3 (Xtensa LX7 @ 240 MHz)
The measurements highlight the real-world benefits of the upgraded LX7 core.
- Upgraded instruction set: The S3 features PIE (Processor Instruction Extensions), adding hardware-level vector processing capability.
- The float champ (below 480 MHz): At 62.62 µs per floating-point FFT — nearly 16,000 computations per second — the ESP32-S3 is a formidable chip for real-time edge audio processing, DSP filtering, and spectrum analysis.
- Integer IFFT beast: It processes the
int16_tIFFT in just 107.96 µs, an outstanding 9,263 IFFTs per second.
👑 STM32H7 (Cortex-M7 @ 480 MHz)
The undisputed performance benchmark of this lineup.
- Hardware double precision: As the only chip tested with a double-precision hardware FPU, it computes
doubleFFTs (238.10 µs) faster than the Arduino Nano or RP2040 can handle simpleint8_tmath! - Blazing clock: Its 480 MHz superscalar, L1-cached pipeline cranks out 42,808 float FFTs every second.
Conclusions
- If you need FFTs, choose a microcontroller with an FPU. On every FPU-equipped board tested, single-precision
floatwas the fastest data type — often by a wide margin — while also being the easiest to work with (no scaling or overflow management). - Integer FFTs only make sense on FPU-less chips such as the RP2040 or the old AVR boards, where
int8_t/int16_tprocessing is 3–4× faster than software floats. - Avoid
doubleon Microcontrollers: .floats are at least 10 times faster - Best value for real-time audio: The ESP32-S3 delivers nearly 16,000 float FFTs per second at a fraction of the cost of an STM32H7 board — plus built-in Wi-Fi and Bluetooth.
The full source code, benchmark sketches, and raw results are available in the fixedpoint-fft repository on GitHub.
0 Comments