esp32-ai
A 28.9 million parameter language model runs on an $8 ESP32-S3 microcontroller entirely on-device, generating simple stories at about 9.5 tokens per second.
展示了系统化的诊断方法(先测量后猜测)、基于风险的架构决策,以及提供可复现的代码和基准测试。
一次真实的排查记录。起因是一个跑在 3.6G 内存小机器上的 Flask 应用,进程内存三天涨到 888M。
结论先放这里,省得你翻到最后:
| 之前 | 之后 | |
|---|---|---|
| Flask 主进程 RSS | 888M(三天) | 68M(稳定) |
| 一次语义检索 | 10.99s | 0.34s |
| 检索结果 | — | 逐位一致,没变 |
两件事其实没什么关系,是排查过程中前后脚撞上的。但它们合起来说明一个道理:动手优化之前先测量,你以为的瓶颈通常不是瓶颈。我在这篇里两次猜错,都写出来了。
一个 Flask 应用,功能是笔记 + 对话,带语义检索(bge 中文小模型做 embedding,BM25 做关键词兜底)。机器 3.6G 内存。
现象是主进程 RSS 一路涨:
重启后 79M
17 分钟后 662M
三天后 888M
看起来像内存泄漏。但"看起来像"不等于是。
如果真是泄漏,那一定有某个代码路径反复执行、每次留下一点。最省事的验证方式是按真实流量比例打请求,看 RSS 和对象数怎么走。
我没用 py-spy、没上 tracemalloc,就用 Flask 自带的 test_client 在独立进程里跑:
import gc, collections, time
def rss_kb():
for line in open('/proc/self/status'):
if line.startswith('VmRSS:'):
return int(line.split()[1])
def obj_profile():
c = collections.Counter()
for o in gc.get_objects():
c[type(o).__name__] += 1
return c
import app as A
client = A.app.test_client()
gc.collect()
base_rss, base_obj = rss_kb(), obj_profile()
# 1) 先测后台线程空转:什么都不请求,只等
time.sleep(45)
gc.collect()
print('空转 45 秒:', (rss_kb() - base_rss) / 1024, 'M')
# 2) 再按真实流量比例逐个端点压
for method, url in ENDPOINTS:
gc.collect(); before = rss_kb()
for _ in range(150):
client.get(url)
gc.collect(); after = rss_kb()
print(f'{url:42} {(after-before)/1024:+.1f}M')
# 3) 最后看对象类型的净增长
gc.collect()
for name, n in (obj_profile() - base_obj).most_common(12):
print(f'{name:28} +{n}')
完整脚本在 code/memprobe.py。
结果:
空转 45 秒后 +0.3M
/api/notes_by_date +0.3M
/api/profile -0.3M
/api/feed +0.8M
/api/tags/list +0.0M
/api/messages +2.0M
/api/shelf +0.0M
...
1200 个请求总计 +3M
对象数增长 top:
function +23
ReferenceType +19
builtin_function_or_method +19
SplitResult +18
list +4
读接口是干净的。 1200 个请求只涨 3M,对象增长几十个而且全是模块导入的正常副产物(function、ModuleSpec、SourceFileLoader 这些是首次走到某分支时 import 带进来的,一次性)。后台线程空转也干净。
那 888M 从哪来的?
既然不是反复累积,那就是某个东西一次性吃掉一大块然后赖着不走。分层测最直接:
def rss_mb():
for line in open('/proc/self/status'):
if line.startswith('VmRSS:'):
retu
把 530M 的 ML 栈请出 Flask 主进程,顺便让语义检索快了 32 倍。一次真实排查的完整记录:怎么证伪内存泄漏、为什么拆进程而不换 ONNX、以及 profile 如何打脸我的直觉。
根据分类、Topic 和编程语言匹配的相似项目。
A 28.9 million parameter language model runs on an $8 ESP32-S3 microcontroller entirely on-device, generating simple stories at about 9.5 tokens per second.
Deltafin is a research project that runs the 2.8-trillion-parameter Mixture-of-Experts model Kimi K3 on a single Apple Silicon Mac (e.g., M1 Max with 64 GB) at about 16 seconds per token, using exact, reproducible inference with local or streaming expert loading.

A comprehensive guide for building and configuring a high-end local machine to run state-of-the-art LLMs, with detailed hardware choices, BIOS tuning, and Docker-based model serving.