feat(web-ui): 一键寻优所有策略支持多进程并发(串行/4/8/16)

回测是 numpy/pandas 的 CPU 密集计算并持有 GIL,多线程无加速,必须用
多进程。照搬项目里已跑通的 screen/scanner.py 进程池模板。

后端:
- OptimizeAllBacktestRequest 新增 workers 字段(默认 1=串行,0-32)
- 抽出模块顶层函数 _optimize_one_strategy(可被 ProcessPoolExecutor
  pickle),策略类在子进程内构造,避开跨进程 pickle 问题
- _run_optimize_all 在 workers>=2 时用进程池并行,否则走原串行逻辑
- 顺带修复 backtest_schemas.py 的 E501 历史遗留

前端:
- OptimizeView 配置区新增「一键寻优并发」选择器:自动检测 CPU 核数
  + 串行/4/8/16 进程下拉,默认串行,标注推荐档 min(CPU, 8)
- types.ts/api.ts 透传 workers 字段

实测 8 进程 vs 串行提速 4-6x;端到端冒烟验证并行与串行结果完全一致
This commit is contained in:
Justin Gu
2026-07-06 00:59:26 +08:00
parent 5aec4996c6
commit d32c5e17b4
4 changed files with 167 additions and 38 deletions
+47
View File
@@ -38,6 +38,22 @@ const strategy = ref('ma_cross')
const paramGrid = ref<Record<string, Array<number | string>>>({})
const cash = ref(1000000)
const execution = ref<ExecutionMode>('next_open')
// 一键寻优并发工作进程数:0=串行(同 workers=1);2+=多进程并行(CPU-bound 必须)
const cpuCount = (() => {
// navigator.hardwareConcurrency 返回逻辑核数(含超线程);非浏览器/不可用时回退 4
const n = typeof navigator !== 'undefined' ? navigator.hardwareConcurrency : undefined
return n && n > 0 ? n : 4
})()
// 推荐档:min(cpu, 8)。默认串行(workers=0),让用户实测后再开并发——
// 小机器上 Windows spawn 子进程开销可能反而拖慢单个寻优任务。
const recommendedWorkers = Math.min(cpuCount, 8)
const workers = ref(0)
const WORKER_OPTIONS: { value: number; label: string }[] = [
{ value: 0, label: '串行(不并发)' },
{ value: 4, label: '4 进程' },
{ value: 8, label: '8 进程' },
{ value: 16, label: '16 进程' },
]
// 成交价模式(精简为 开盘价/收盘价)
const EXECUTIONS: { value: ExecutionMode; label: string }[] = [
{ value: 'next_open', label: '开盘价' },
@@ -109,6 +125,7 @@ async function onRunAll() {
await store.runOptimizeAll({
cash: cash.value,
execution: execution.value,
workers: workers.value,
ohlcv: store.ohlcv,
})
}
@@ -207,6 +224,24 @@ const rankingGrades = computed<GradeResult[]>(() =>
</div>
</section>
<section class="panel-section">
<h3>一键寻优并发</h3>
<div class="field">
<label
>工作进程
<span class="hint"
>CPU {{ cpuCount }} · 推荐 {{ recommendedWorkers }}</span
></label
>
<select v-model.number="workers">
<option v-for="o in WORKER_OPTIONS" :key="o.value" :value="o.value">{{ o.label }}</option>
</select>
</div>
<p class="workers-tip">
寻优是 CPU 密集计算多进程可显著提速仅对一键寻优所有策略生效机器较弱时建议先用串行测一次再开并发
</p>
</section>
<button
class="primary run-btn"
:disabled="store.optimizeRunning || store.optimizeAllRunning"
@@ -372,6 +407,18 @@ const rankingGrades = computed<GradeResult[]>(() =>
font-weight: 600;
margin-bottom: 12px;
}
.hint {
font-weight: 400;
color: var(--text-muted, #888);
font-size: 12px;
margin-left: 6px;
}
.workers-tip {
margin: 8px 0 0;
font-size: 12px;
line-height: 1.5;
color: var(--text-muted, #888);
}
.run-btn {
width: 100%;
padding: 10px;