Files
qlib/backend/app/cli/run_job.py
T
Simon 195f5d41f4 perf(backend): 内存优化三项——全市场研究不再占满 8G
1) 数据装配流式+列裁剪:Repository 新增 stream_range_many_columns(只 SELECT
   所需列、SQL 侧转 REAL、yield_per 分批),引擎按 required_columns 取数
   (LocalEngine 仅 close+因子字段),消除 ORM/Decimal 全量物化;
2) 研究 Job 独立子进程执行(job.mode=subprocess):python -m app.cli.run_job
   在子进程内设 RLIMIT_AS 上限,OOM 归档 failed 而非拖垮 API worker;
   子进程异常退出由父进程补记 failed;并发上限 2;
3) 服务启动清理:残留 queued/running Job 标记 failed(防永久 running)。

实测同款全市场回测:uvicorn worker RSS 稳定 ~220MB,任务峰值内存由 4.1GB+
降至 ~470MB,24s 完成并归档(此前 43s 未完成即 OOM)。
新增/更新测试 96 passed,ruff 干净。
2026-09-06 22:12:44 +08:00

48 lines
1.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""独立进程执行研究 Job —— 内存隔离的执行体(内存优化专项)。
由 job_executor._run_in_subprocess 以 `python -m app.cli.run_job <JOB_ID>` 拉起
(cwd=backend/),入口先施加 RLIMIT_AS 上限再导入重依赖,随后执行与 API 内
execute_job 完全相同的状态机 / Experiment 归档逻辑。
环境变量:
QLIB_JOB_MEM_LIMIT_GB —— 子进程虚拟地址空间上限(GB);父进程 spawn 时写入,
手动直接运行本命令而未设置时回退到 Settings(config job.max_memory_gb)。
"""
from __future__ import annotations
import os
import sys
def _apply_memory_limit() -> None:
"""设置 RLIMIT_AS(Linux)。达到上限时分配抛 MemoryError → Job 归档 failed,
而不是让内核 OOM 杀掉整机(8G Pi5 上保护同机其它服务)。"""
try:
gb = int(os.environ.get("QLIB_JOB_MEM_LIMIT_GB") or "")
except (TypeError, ValueError):
from app.core.config import get_settings
gb = get_settings().job_memory_limit_gb
limit = gb * 1024**3
import resource
resource.setrlimit(resource.RLIMIT_AS, (limit, limit))
def main(argv: list[str] | None = None) -> int:
args = list(sys.argv[1:] if argv is None else argv)
if len(args) < 1:
print("用法: python -m app.cli.run_job <JOB_ID>", file=sys.stderr)
return 2
_apply_memory_limit()
# 先设内存上限再导入重依赖(pandas / sqlalchemy 等)
from app.application.services.job_executor import default_factories, execute_job
execute_job(args[0], **default_factories())
return 0
if __name__ == "__main__":
raise SystemExit(main())