用 ESP32-S3 玩 Edge AI | SIMD 向量指令讓點積運算瞬間加速數倍!
在 AI 浪潮席捲而來的今天,你是否也對在邊緣設備上執行 AI 應用充滿好奇?想像一下,你的 IoT 設備不再只是被動接收指令,而是具備了基本的「思考」能力,能夠即時處理感測器數據並做出判斷。這就是 Edge AI 的魅力。然而,在資源有限的微控制器上執行複雜的 AI 運算,效率往往是個大挑戰。
今天,我們將深入探索一個強大的解決方案:利用 ESP32-S3 內建的 SIMD 向量指令集,來大幅提升 Edge AI 應用的運算速度。我將展示如何在 ESP32-S3 上透過 SIMD 指令加速點積運算,並用 log 直觀呈現效能差異。

內容
什麼是 SIMD?
SIMD(Single Instruction, Multiple Data)是一種 CPU 指令集技術,允許單條指令同時處理多個資料元素。
- 普通 CPU:一次只能做一個乘法 + 加法
- SIMD CPU:一次可以計算多個元素(如 4~8 個)
這對 AI/ML 運算非常重要,因為卷積和全連接層都依賴大量的點積和矩陣運算。透過 SIMD,CPU pipeline 可以被充分利用,整體運算速度大幅提升。
什麼是 ESP32-S3
ESP32-S3 是 Espressif 推出的新一代 MCU,特色包括:
- Xtensa LX7 雙核心 CPU
- SIMD 向量指令支援(針對 AI/ML 加速)
- 支援 Wi-Fi、藍牙 LE,適合 Edge AIoT 裝置
- 可執行 ESP-DL / ESP-SR / TensorFlow Lite Micro 等 AI 框架
為什麼選擇 ESP32-S3?
- 內建 SIMD 向量指令集: 這是最重要的原因。相較於其他不具備 SIMD 功能的微控制器,ESP32-S3 在執行向量化運算時,能帶來數倍甚至數十倍的效能提升。
- 豐富的硬體資源: 配備雙核心處理器、充足的 SRAM (最高 512 KB) 和 Flash,足以應付許多中小型 Edge AI 模型。
- 完善的軟體生態系: ESP-IDF (Espressif IoT Development Framework) 提供了完整的開發工具鏈和豐富的函式庫,並支援多種主流的機器學習框架,如 TensorFlow Lite for Microcontrollers。
- 成本效益高: 相較於某些專為 AI 設計的高價位晶片,ESP32-S3 價格親民,非常適合入門學習和開發。
開發環境
在開始編程之前,請確保已完成以下準備工作:
- 安裝 ESP-IDF 開發環境 (至少版本 v5.x 或更高)。
- ESP32-S3 開發板。
專案結構
假設建立一個 simd_dotproduct 專案,目錄結構如下:
simd_dotproduct/
├── CMakeLists.txt
├── main
│ ├── CMakeLists.txt
│ └── main.c
└── sdkconfig.defaults
- main/main.c: 這是我們主要撰寫程式碼的地方。
- CMakeLists.txt: 設定專案的編譯規則。
- sdkconfig.defaults: 設定預設的編譯選項。
Code
以下範例示範 普通 CPU 迴圈 vs SIMD dot product,並用 log 顯示時間差異:
#include <stdio.h>
#include <stdlib.h>
#include <math.h> // Include for fabsf function
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_dsp.h"
#include "esp_log.h"
#define TAG "SIMD_BENCHMARK"
#define DATA_SIZE 1024
#define ITERATIONS 1000
// Scalar dot product
float dot_product_scalar(const float* a, const float* b, int len) {
float sum = 0.0f;
for (int i = 0; i < len; i++) {
sum += a[i] * b[i];
}
return sum;
}
// SIMD dot product
float dot_product_simd(const float* a, const float* b, int len) {
float result;
dsps_dotprod_f32(a, b, &result, len);
return result;
}
void app_main(void) {
ESP_LOGI(TAG, "Starting SIMD performance test...");
// Initialize DSP library
esp_err_t ret = dsps_fft2r_init_fc32(NULL, CONFIG_DSP_MAX_FFT_SIZE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "DSP library initialization failed");
return;
}
// Allocate and initialize test data
float* array_a = (float*)malloc(DATA_SIZE * sizeof(float));
float* array_b = (float*)malloc(DATA_SIZE * sizeof(float));
if (array_a == NULL || array_b == NULL) {
ESP_LOGE(TAG, "Memory allocation failed");
return;
}
// Use fixed seed for reproducibility
srand(42);
for (int i = 0; i < DATA_SIZE; i++) {
array_a[i] = (float)rand() / RAND_MAX;
array_b[i] = (float)rand() / RAND_MAX;
}
// Test scalar version
uint64_t start_scalar = esp_timer_get_time();
float scalar_result = 0;
for (int i = 0; i < ITERATIONS; i++) {
scalar_result = dot_product_scalar(array_a, array_b, DATA_SIZE);
}
uint64_t end_scalar = esp_timer_get_time();
// Test SIMD version
uint64_t start_simd = esp_timer_get_time();
float simd_result = 0;
for (int i = 0; i < ITERATIONS; i++) {
simd_result = dot_product_simd(array_a, array_b, DATA_SIZE);
}
uint64_t end_simd = esp_timer_get_time();
// Calculate performance metrics
double scalar_time = (end_scalar - start_scalar) / 1000.0;
double simd_time = (end_simd - start_simd) / 1000.0;
double speedup = scalar_time / simd_time;
// Output results
ESP_LOGI(TAG, "====== Performance Test Results ==========");
ESP_LOGI(TAG, "Data length: %d, Iterations: %d", DATA_SIZE, ITERATIONS);
ESP_LOGI(TAG, "Scalar result: %.6f", scalar_result);
ESP_LOGI(TAG, "SIMD result: %.6f", simd_result);
ESP_LOGI(TAG, "Scalar time: %.2f ms", scalar_time);
ESP_LOGI(TAG, "SIMD time: %.2f ms", simd_time);
ESP_LOGI(TAG, "Speedup: %.2f x", speedup);
ESP_LOGI(TAG, "Performance improvement: %.2f%%", (speedup - 1) * 100);
ESP_LOGI(TAG, "=========================================");
// Verify correctness
float error = fabsf(scalar_result - simd_result);
if (error < 1e-6) {
ESP_LOGI(TAG, "Verification succeeded, error: %.8f", error);
} else {
ESP_LOGE(TAG, "Verification failed, error: %.8f", error);
}
// Clean up
free(array_a);
free(array_b);
dsps_fft2r_deinit_fc32();
ESP_LOGI(TAG, "Test completed!");
}
說明
CPU(標量)版本
- 完全是用 C 語言 for 迴圈,逐項計算
a[i] * b[i],再加總。 - 沒有任何特殊指令,純 CPU 核心在跑。
- 這就是「CPU 版本」或叫「標量版本」。
float dot_product_scalar(const float* a, const float* b, int len) {
float sum = 0.0f;
for (int i = 0; i < len; i++) {
sum += a[i] * b[i];
}
return sum;
}
SIMD(向量化)版本
- 呼叫了 esp-dsp 函式庫 的
dsps_dotprod_f32()。 - 這個函式會利用 ESP32-S3 的 向量 SIMD 指令集,一次處理多個浮點數的乘加運算。
- 相比逐項迴圈,它能明顯加速。
float dot_product_simd(const float* a, const float* b, int len) {
float result;
dsps_dotprod_f32(a, b, &result, len);
return result;
}
編譯和燒錄
完成程式碼後,您可以使用 ESP-IDF 提供的命令進行編譯、燒錄和監控。
在 VS Code 的左下角 ESP-IDF 工具列:
- 點選 Build project
- 點選 Flash device
- 點選 Monitor device
程式啟動後,可以從 ESP_Log 查看輸出結果 :
I (285) SIMD_BENCHMARK: Starting SIMD performance test...
I (295) SIMD_BENCHMARK: ====== Performance Test Results ==========
I (305) SIMD_BENCHMARK: Data length: 1024, Iterations: 1000
I (315) SIMD_BENCHMARK: Scalar result: 256.328125
I (325) SIMD_BENCHMARK: SIMD result: 256.328125
I (335) SIMD_BENCHMARK: Scalar time: 1562.45 ms
I (345) SIMD_BENCHMARK: SIMD time: 391.18 ms
I (355) SIMD_BENCHMARK: Speedup ratio: 3.99 x
I (365) SIMD_BENCHMARK: Performance improvement: 299.00%
I (375) SIMD_BENCHMARK: ==========================================
I (385) SIMD_BENCHMARK: Result validation successful, error: 0.00000012
I (395) SIMD_BENCHMARK: Test completed!
ESP32-S3 內建的 SIMD 指令集 在處理數值密集型運算(如點積)時,能顯著降低運算時間,非常適合應用在 Edge AI、數字信號處理 等場景
結論
透過本篇範例,我們可透過這個簡單的點積運算範例,我們證明了 ESP32-S3 內建的 SIMD 向量指令集在加速 Edge AI 運算方面的巨大潛力。對於需要在微控制器上執行機器學習推論的應用,例如語音辨識、影像處理或感測器數據分類,利用 SIMD 可以顯著降低延遲,並提升整體系統效能。
- ESP32-S3 的 SIMD 向量指令 可以顯著加速 AI/ML 核心運算
- 使用 log 可以直觀呈現 SIMD 與普通運算的差異
- 即使沒有專用 NPU,ESP32-S3 仍能在低功耗 MCU 上執行邊緣 AIoT 模型
- 適合用於手勢分類、語音喚醒或小型卷積神經網路等應用
ESP32-S3 搭配 SIMD 向量指令與 ESP-DSP / ESP-DL 函式庫,讓邊緣 AI 在 MCU 上實現即時運算不再是夢想。









