mineru-api leaks ~nproc OS threads per parsed document (OpenMP teams per ThreadPoolExecutor thread); OMP_NUM_THREADS fixes it
Search before asking | 提交之前请先搜索
- I have searched the MinerU Readme and found no similar bug report.
- I have searched the MinerU Issues and found no similar bug report.
- I have searched the MinerU Discussions and found no similar bug report.
Consult the online AI assistant
- Consulted, no solution found.
Description of the bug | 错误描述
TL;DR: mineru-api leaks ~nproc OS threads per parsed document until it plateaus at roughly 32 × nproc threads (~3000 on a 96-core host). They are not Python threads: they are OpenMP (libgomp) worker teams. Every thread that calls a torch CPU op gets its own persistent OpenMP team of nproc - 1 workers, and mineru-api runs its CPU work through asyncio.to_thread() (a ThreadPoolExecutor with up to 32 workers), so each new pool thread costs another ~95 threads that are never released. Setting OMP_NUM_THREADS fixes it; the Docker image / mineru-api does not set any default.
中文摘要: mineru-api 每解析一份文档就多出约 nproc 个 OS 线程,最终封顶在约 32 × nproc(96 核机器上约 3000)。这些不是 Python 线程,而是 libgomp 的 OpenMP 工作线程组:每个调用过 torch CPU 算子的线程都会获得一组常驻的 nproc-1 个 OpenMP 工作线程,而 mineru-api 通过 asyncio.to_thread()(最多 32 个池线程)跑 CPU 部分,于是每用到一个新的池线程就固定多出约 95 个线程且不回收。设置 OMP_NUM_THREADS 即可解决;镜像和 mineru-api 目前没有任何默认值。
Measurements (same 1-page, 2.5 KB PDF parsed three times through mineru-api, backend hybrid-engine; thread count = ls /proc/<mineru-api pid>/task | wc -l):
default (no OMP_NUM_THREADS) |
OMP_NUM_THREADS=8 + MINERU_INTRA_OP_NUM_THREADS=8 |
|
|---|---|---|
| threads right after startup (models preloaded) | 263 | 126 |
| after parse #1 | 461 (+198, includes lazy model init) | 236 (+110, includes lazy model init) |
| after parse #2 | 558 (+97) | 245 (+9) |
| after parse #3 | 655 (+97) | 254 (+9) |
+97 ≈ nproc (96) + 1 per document in the default configuration; +9 ≈ OMP_NUM_THREADS + 1 with the cap.
Evidence that these are OpenMP threads and not Python threads:
py-spy dumpon the same process shows only ~12 Python threads (MainThread,MPClientEngineMonitor, a handful ofThreadPoolExecutor-*workers,QueueFeederThread), while/proc/<pid>/taskshows 600+ entries.libgomp.so.1andlibtorch_cpu.soare mapped in the process. libgomp keeps one worker team per master thread that has entered a parallel region, for the lifetime of that master thread;ThreadPoolExecutorworkers used byasyncio.to_thread()live forever, so their teams are never torn down.- The growth stops exactly when the cap is applied and the per-document increment shrinks to the cap value.
Impact: on a long-running mineru-api we had to add an external watchdog that restarted the container whenever the thread count exceeded 1500 (317 preventive restarts in three weeks). Thread counts this high also mean heavy CPU oversubscription (every OpenMP team is sized to the whole machine), which hurts throughput on many-core hosts.
Suggested fix: set a sane default in mineru-api (and the Docker image), e.g. torch.set_num_threads(min(8, os.cpu_count())) at startup or OMP_NUM_THREADS/MINERU_INTRA_OP_NUM_THREADS defaults, and/or route torch CPU work through a dedicated single worker thread instead of the shared default executor so only one OpenMP team ever exists. At minimum, documenting OMP_NUM_THREADS for mineru-api deployments would help.
How to reproduce the bug | 如何复现
- Run
mineru-apiin Docker (image built frommineru==3.4.5) on a many-core host, e.g.:mineru-api --host 0.0.0.0 --port 8000 --enable-vlm-preload True --gpu-memory-utilization 0.10 --kv-cache-memory-bytes 2147483648 --max-num-seqs 32 - Note the thread count of the
mineru-apiprocess:ls /proc/$(pgrep -f mineru-api | head -1)/task | wc -l - POST any small PDF to
/file_parse(backendhybrid-engine;pipelineshows the same behaviour) three or more times. - Re-check the thread count after each request: it grows by roughly
nprocper document and never goes down while the process is idle. - Restart with
OMP_NUM_THREADS=8: the per-document growth drops to ~9 and plateaus at a few hundred threads.
Any PDF reproduces it (we used a 1-page 2.5 KB text PDF); the file content is irrelevant.
Operating System Mode | 操作系统类型
Linux
Operating System Version | 操作系统版本
Ubuntu 24.04.4 LTS (kernel 6.8.0-136-generic), Docker; VM with 96 vCPUs
Python version | Python 版本
3.12 (3.12.13)
Software version | 软件版本 (mineru --version)
>=2.5 — mineru 3.4.5 (torch 2.11.0+cu130, vllm 0.21.0, onnxruntime 1.28.0, transformers 4.57.6)
Backend name | 解析后端
vlm (hybrid-engine via mineru-api; pipeline is affected the same way since the leak is in the CPU/OpenMP path)
Device mode | 设备模式
cuda (NVIDIA RTX PRO 6000 Blackwell Server Edition)
Source: opendatalab/MinerU