Skip to content

优化围棋搜索与推理,降低训练和渲染开销 - #3

Merged
Barbatosishere merged 3 commits into
mainfrom
perf/go-search-inference
Oct 7, 2026
Merged

Barbatosishere merged 3 commits into
mainfrom
perf/go-search-inference

Conversation

@Barbatosishere

@Barbatosishere Barbatosishere commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

围棋搜索、CPU 推理和多局训练中反复构建棋群、复制棋盘和分配临时数组,增加了 CPU 与 GC 开销。本次复用线程内缓冲、共享评估器并缓存搜索节点统计,同时补齐规则、并发和 GPU 压测验证。

改动

  • 优化候选落子、棋群与气的分析、提子模拟和增量 Zobrist 哈希;复用战术搜索缓冲,修复棋群去重并保留超时检查。
  • 减少搜索树复制,缓存子节点快照并复用自对弈采样权重;保持棋盘、候选列表和推理结果的隔离。
  • CPU 前向复用中间缓冲并按权重行计算;仅需价值时跳过策略头,GPU 初始化失败后同样使用该 CPU 路径。
  • OpenCL 共享上下文、缓存模型权重与设备缓冲,并合并并发推理批次;子块权重与偏置仅上传 9 组,由内核按所属大块读取,避免扩展为 81 组。
  • GPU 初始化失败后冷却 30 秒重试,现有评估器也能恢复;最后一个使用者退出时释放设备缓冲和原生句柄,已释放的评估器不会因查询状态重新获取资源。后续新使用者会重新创建上下文。
  • 推理等待线程中断时,移除尚未消费的请求;已进入批次的请求等待读取结束并保留计算结果和中断状态,防止调用方提前复用输入缓冲。
  • 训练缓存使用独立的设备权重修订号,避免相同 checkpoint 版本替换后继续使用旧 GPU 权重;上传开始前使旧缓存失效,上传失败后下一次请求会完整重传。
  • 拒绝 NaN、无穷大和非法范围的学习率、L2、梯度裁剪及动量参数,避免训练把模型权重污染为 NaN。
  • 自对弈和对抗训练共享评估器,完善资源释放与模型读写同步;将 JNA 限定为训练与测试运行依赖,完善 GPU 状态输出、训练步数及学习率下限。
  • 缓存游戏筛选结果与圆形扫描线,将粒子更新移到 tick;设置导入后台执行并防止重复选择,处理游戏目录不可用的情况。

验证

  • gradlew.bat test --no-daemon build --no-build-cache --offline --max-workers=1 --console=plain:成功,227 项测试,0 失败、0 错误、0 跳过。
  • git diff --cached --check:通过。
  • 新增 GPU 压测覆盖:24 个并发 worker 执行 3,072 次推理;12 次 GPU/CPU 训练更新(SGD、动量和 checkpoint 重载);6 轮 OpenCL 上下文创建/释放;批次大小 1/64/65/3/129/2;相同版本模型替换、失败上传后的缓存恢复。
  • NVIDIA GeForce RTX 2060 实测 GPU/CPU 结果一致,压测期间无失败测试。
  • 覆盖 CPU 推理、候选生成、特征提取、提子/自杀/劫模拟、固定随机种子采样、搜索树隔离和渲染回归。
  • 局部微基准:156 节点树复制分配量约 623 KB → 325 KB;固定棋盘 CPU 价值推理约 16.4 KB → 48 B/次;256 子节点选择快照约 1,040 B → 0 B/次。以上仅反映局部内存分配,不代表整局速度或棋力提升。

GPU 结果一致性已在上述设备验证;其他厂商和驱动仍需各自验证。无可用设备的环境会明确跳过新增硬件一致性测试。

@kilo-code-bot

kilo-code-bot Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (6 files)
  • src/main/java/com/wzz/game_console/client/screens/games/gogame/NeuralEvaluator.java
  • src/main/java/com/wzz/game_console/client/screens/games/gogame/OpenCLBackend.java
  • src/main/java/com/wzz/game_console/client/screens/games/gogame/GoAdversarialTrainer.java
  • src/main/java/com/wzz/game_console/client/screens/games/gogame/GoSelfPlayTrainer.java
  • src/test/java/com/wzz/game_console/client/screens/games/gogame/GoGpuStressTest.java
  • src/test/java/com/wzz/game_console/client/screens/games/gogame/GoTrainingRegressionTest.java
Previous Review Summaries (4 snapshots, latest commit a09a647)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit a09a647)

Status: No Issues Found | Recommendation: Merge

Overview

This incremental commit (a09a647) fixes the OpenCL retry, resource-release, and inference-interrupt race. All four findings from the prior review were re-verified against the changed lines and are resolved:

Prior finding File Resolution in this change
sharedFailure never reset (Medium) OpenCLBackend.java Per-process cooldown (RETRY_DELAY_NANOS = 30s) plus sharedFailure = null on successful acquire; OpenCLBackend.acquireShared retry-gated.
close() never freed native handles (Medium) OpenCLBackend.java:close() Last owner now calls shared = null; destroyNative(); orphan (non-shared) close() also calls destroyNative().
9× weight duplication in ensureWeights (Low) OpenCLBackend.java dSubW/dSubB allocated at 9-block size; ensureWeights uploads subW/subB directly; sub_fwd kernel indexes block = s/9.
Interrupt race: completeExceptionally vs complete (Low) NeuralEvaluator.java Removed completeExceptionally. In-flight waiters now join() (preserving the batch result); queued waiters are remove()+cancel.

Verified OPENCLBackend lock ordering is consistent (SHARED_LOCK → lifecycleLock) with no deadlock path, and that native teardown in destroyNative() waits on the in-flight lifecycleLock before freeing buffers. The new forward() isInterrupted fast-path (zero result) and interrupt preservation are exercised by OpenCLLifecycleRegressionTest and CpuInferenceRegressionTest. No new issues were introduced on changed lines.

Notes

  • No GPU was available in this offline sandbox, so the native parity test (compactSubWeightsMatchCpuAcrossBatchSizesAndModelSwitchesOnGpu) was reviewed for logic only; the kernel/buffer math (9 blocks × 36 × 16 weights, block=s/9 indexing, extractSubInputs 81-subregion layout) checks out against cpuForward (subW1[b] per block).
Files Reviewed (4 files)
  • src/main/java/com/wzz/game_console/client/screens/games/gogame/OpenCLBackend.java — full file (changed lines: cooldown, close/destroyNative, compact weights, sub_fwd kernel)
  • src/main/java/com/wzz/game_console/client/screens/games/gogame/NeuralEvaluator.java — full file (changed lines: ensureOpenCL cooldown path, isGpuActive/gpuDeviceName lock scope, detectGpu try-with-resources, interrupt/join handling, isInterrupted fast-path)
  • src/test/java/com/wzz/game_console/client/screens/games/gogame/CpuInferenceRegressionTest.java — full file (changed lines: seed openclFailureAt so the cooldown path keeps the disable-GPU assertion green)
  • src/test/java/com/wzz/game_console/client/screens/games/gogame/OpenCLLifecycleRegressionTest.java — new file, full read (validates cooldown, last-owner teardown, close-waits-for-batch, and the queued/in-flight interrupt contracts)

Previous review (commit 1b0d6cc)

Verdict

Approve with suggestions. The PR delivers substantial, well-executed improvements to GPU inference batching, MCTS tree reuse, training correctness, and UI responsiveness. Three medium-severity concerns below are design decisions that should be explicitly acknowledged or revisited.


Inline Comments

1. OpenCLBackend.java:65 — sharedFailure is never reset (Medium)

if (sharedFailure != null) return null;

Once sharedFailure is set (line 68), it is never cleared for the JVM's lifetime. A temporary GPU failure (driver reload, GPU in use by another process, transient OOM) permanently disables GPU acceleration. Users must restart the JVM. Consider adding a retry path — e.g., clear sharedFailure after a cooldown, or expose a resetGpu() method for the training CLI to call between generations.

2. OpenCLBackend.java:600-605 — close() for shared backend never calls destroyNative() (Medium)

if (retainCount.decrementAndGet() > 0) return;
// 引用归零后仍保留上下文,下一次 acquireShared 直接复用已编译内核。
return;

When retainCount reaches 0, native OpenCL resources (context, queue, program, kernels, all device buffers) are never freed. They persist until OS process termination. The comment "进程退出由 JVM 回收 native 句柄" is misleading — the JVM does not release native OpenCL handles; the OS does when the process exits. For a long-running Minecraft session, this means all GPU memory allocated for the Go evaluator is pinned for the entire game session. This is an acceptable trade-off for a shared singleton, but should be documented more precisely. Consider registering a JVM shutdown hook that calls destroyNative(), or exposing an explicit shutdownAll() for clean teardown.

3. OpenCLBackend.java:332-338 — Weight duplication in ensureWeights (Low)

double[][][] subWGpu = new double[81][][];
for (int si = 0; si < 81; si++) {
    int b = si / 9;
    subWGpu[si] = subW[b];

The 9 weight sets are referenced 9 times each (81 total entries), then flatten3D(subWGpu) copies all 81 entries into a CPU Memory buffer before uploading. This creates a 9× larger flat array than necessary. A GPU-side broadcast kernel (or clEnqueueCopyBuffer with strided offsets) would avoid the CPU-side duplication and reduce upload bandwidth.

4. NeuralEvaluator.java:710-713 — Interrupted thread may discard valid GPU result (Low)

} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    call.done.completeExceptionally(e);
    return null;
}

If the waiting thread is interrupted at this point, call.done.completeExceptionally(e) may race with the flusher thread's call.done.complete(result). If completeExceptionally wins, a successfully computed GPU result is silently discarded and the thread falls back to CPU. While acceptable for the interrupt path, it wastes GPU work. Consider checking call.done.isDone() before calling completeExceptionally, or simply propagate the interrupt without preemptively failing the future.


Positive Observations

Area File Comment
NaN safety NeuralEvaluator.java:1274 !Double.isFinite(norm) guard prevents NaN/Inf gradients from corrupting all weights via the clipping bypass bug
Bias gradient fix NeuralEvaluator.java:1200 gValueB1[i] += dVh[i] corrects a latent bug where the value-head hidden-layer bias was never updated (permanently frozen)
Shared GPU context OpenCLBackend.java:56 acquireShared() eliminates per-evaluator context creation during 30-way self-play — critical for consumer GPUs
Weight upload caching OpenCLBackend.java:331 (ownerId, modelVersion) cache key avoids redundant GPU uploads between forward passes
Batched inference NeuralEvaluator.java:692 coalesceGpuForward + flushGpuInferQueue batches up to 64 concurrent forward passes onto one GPU submission
Lock upgrade NeuralEvaluator.java:206 ReentrantReadWriteLock allows concurrent forward (readLock) instead of serialized ReentrantLock
Tree reuse correctness MCTSGoAI.java:1551 tryReuseTree deep-copies boards and preserves untriedMoves only for matching positions — prevents stale subtree pollution
Thread-local scratch MCTSGoAI.java:919-938 Eliminates per-iteration allocation in the hot search path
FileDialog fix GameSelectorScreen.java:280 Background daemon thread for FileDialog prevents Minecraft main-thread freeze (AWT modality bug)
Click coordinate fix GameSelectorScreen.java:374 Uses actual click (mx, my) against computed card bounds instead of stale hoveredIndex — fixes wrong-game-launch bug
Circle render optimization GameRenderHelper.java:223 Scanline algorithm with AtomicReferenceArray caching reduces fill calls from ~600 to ~2r per circle
JNA dependency fix build.gradle:156 compileOnly instead of embedding JNA — avoids com.sun.jna module conflict with Iris/Sodium/NeoForge
Error containment OpenCLBackend.java:95-99 catch (Throwable) in constructor prevents UnsatisfiedLinkError (an Error, not Exception) from crashing callers

Testing Confirmation

PR description states:围棋规则/并发回归验证通过 213 tests 0 failures. Test files added: CpuInferenceRegressionTest, GoCandidateRegressionTest, GoFeatureRegressionTest, GoSimulationRegressionTest, GoTrainingRegressionTest, MCTSGoAIRegressionTest, GameRenderHelperTest.

Files Reviewed

  • OpenCLBackend.java (full read)
  • NeuralEvaluator.java (full read)
  • MCTSGoAI.java (full read)
  • GameSelectorScreen.java (full read)
  • GameRenderHelper.java (full read)
  • build.gradle (full read)
  • GoTrainingMain.java, GoAdversarialMain.java, GoAdversarialTrainer.java, GoSelfPlayTrainer.java (partial reads)

Recommendation

Merge the PR. Address the three Medium issues in a follow-up:

  1. Reset sharedFailure on a retry path or after a cooldown
  2. Add a JVM shutdown hook (or explicit shutdownAll()) to call destroyNative() for the shared backend
  3. Avoid the 9× CPU-side weight duplication in ensureWeights

Previous review

This review did not finish. The model reached its output limit before it
could write the review — a reasoning model can spend the whole budget thinking.
Re-run the review, or lower the model's thinking effort, and it should get
further. Any inline comments below are from an earlier review.

Previous review

This review did not finish. The model reached its output limit before it
could write the review — a reasoning model can spend the whole budget thinking.
Re-run the review, or lower the model's thinking effort, and it should get
further. Any inline comments below are from an earlier review.


Reviewed by free · Input: 452.4K · Output: 10.4K · Cached: 190.1K

@Barbatosishere
Barbatosishere merged commit 8864425 into main Oct 7, 2026
2 checks passed
@Barbatosishere
Barbatosishere deleted the perf/go-search-inference branch October 7, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant