Skip to content

[Intel NPU/GPU] Add Windows & Linux Intel NPU & GPU support - #1171

Open
Looong01 wants to merge 44 commits into
lightvector:masterfrom
Looong01:Intel_NPU
Open

[Intel NPU/GPU] Add Windows & Linux Intel NPU & GPU support#1171
Looong01 wants to merge 44 commits into
lightvector:masterfrom
Looong01:Intel_NPU

Conversation

@Looong01

@Looong01 Looong01 commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds and hardens the Windows & Linux Intel NPU path for KataGo using the ONNX backend with ONNX Runtime + OpenVINO Execution Provider, and updates docs/config guidance for an end-to-end workflow.

It also improves failure behavior for non-ONNX builds and simplifies Windows & Linux dependency handling.

What Changed

1) ONNX backend and OpenVINO provider support

  • Added/updated ONNX Runtime provider selection via onnxProvider (cpu, openvino, cuda, tensorrt, migraphx, coreml).
  • Added/updated OpenVINO-specific runtime options:
    • onnxOpenVINODeviceType
    • onnxOpenVINODeviceId
    • onnxOpenVINOCacheDir
    • onnxOpenVINOEnableNPUFastCompile (best-effort; depends on ORT build support)
  • Supports both:
    • loading raw .onnx models directly
    • loading .bin/.bin.gz models via internal conversion to ONNX graph

2) exportonnx command behavior

  • exportonnx is available in ONNX builds and exports fixed-size ONNX models.
  • Default export board size is 19x19 (-x/-y can override).
  • In non-ONNX builds, exportonnx now returns a clear error instead of failing ambiguously.

3) Config safety for non-ONNX binaries

  • In non-ONNX builds, forcing onnx* config keys now fails fast with a clear message.
  • Prevents silent misconfiguration when users accidentally pass ONNX-only config into CUDA/OpenCL/Eigen/etc builds.

4) CMake dependency flow

  • Kept ONNX runtime root wiring via ONNXRUNTIME_ROOT (defaulting to cpp/external/onnxruntime-win-x64-openvino and cpp/external/onnxruntime-linux-x64-openvino).
  • Added/updated automatic dependency fetch flow for Windows & Linux builds (zlib, onnx, protobuf) through vcpkg when enabled.
  • ONNX runtime DLLs or SOs are copied to output dir during build on Windows or Linux.

5) Documentation updates

  • Compiling.md:
    • Added explicit Windows & Linux Intel NPU setup steps:
      • Visual Studio Community or VS 2026 Build Tools (Desktop C++)
      • Intel NPU driver install
      • OpenVINO archive install
      • ONNX Runtime build with OpenVINO EP (use_openvino=NPU)
    • Added the exact file-copy checklist into cpp/external/onnxruntime-win-x64-openvino.
    • Added minimal ONNX backend build command.
  • README.md:
    • Added Intel NPU quick-start section for ONNX/OpenVINO.
    • Added minimal commands for:
      • exportonnx (default 19x19)
      • benchmark
      • gtp

Behavior Notes

  • Multi-device mapping (onnxDeviceToUseThread*) is mainly intended for ONNX providers like CUDA/TensorRT/MIGraphX.
  • OpenVINO Intel NPU usage is typically single-device.

Validation

  • ONNX build compiles successfully on Windows & Linuix.
  • exportonnx works from .bin/.bin.gz -> .onnx.
  • benchmark/gtp run with onnxProvider=openvino and onnxOpenVINODeviceType=NPU.
  • Non-ONNX binaries now correctly reject ONNX-only config keys.

@Looong01

Looong01 commented Mar 16, 2026

Copy link
Copy Markdown
Contributor Author

This is screenshot of Sabaki testing:
屏幕截图 2026-03-16 184650

And the binary release here: https://github.com/Looong01/KataGo-Multi-backends/releases/tag/v1.16.4-openvino

@Looong01

Looong01 commented Mar 16, 2026

Copy link
Copy Markdown
Contributor Author

I partially referenced the code from #1164, and I am very grateful to @ChinChangYang

@seniorfish

Copy link
Copy Markdown
Contributor

Looong01 I discovered an issue! I noticed a significant discrepancy between the total amount of visits and the sum of the visits of each candidate move. After selecting a move, its cache size matches the displayed visits amount (with only a very small amount remaining in the cache).

@foxrainowo
From what I’ve observed, this is most likely not a KataGo issue, but a GUI quirk. The LizzieYzy GUI, for the sake of simplicity, hides some low-visit candidate moves by default. If you’d like to see all candidates, go to Settings → Config or press Shift+X to open the config page(综合设置), and set “limit max candidates(限制选点个数)” to 0. That will display every candidate move. And from my own calculations, there’s no discrepancy between the total visit count and the sum of visits across all candidate moves.

@foxrainowo

Copy link
Copy Markdown

@seniorfish
Oh, thank you!

@Looong01 Looong01 changed the title [Intel NPU] Add Windows & Linux Intel NPU support [Intel NPU/GPU] Add Windows & Linux Intel NPU & GPU support Aug 5, 2026
@Looong01

Looong01 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Summary

Adapts the Intel_NPU branch's custom ONNX Runtime backend to the v1.16.5→v1.17.1 upstream sync. The
upstream merge (Merge branch 'master' into Intel_NPU) pulled in TensorRT's new "build via ONNX
graph" path, which added upstream's own cpp/neuralnet/onnxmodelbuilder.cpp — same filename as this
fork's ONNX-Runtime-specific builder, resolved as an add/add conflict in favor of upstream's version.
That silently deleted the fork's builder and changed NeuralNet::createComputeContext's signature
(10 params → 8, ConfigParser& added), leaving USE_BACKEND=ONNX unable to compile.

The rewrite follows the design of the not-yet-merged upstream PR #1222 ("Add ONNX
Runtime backend support") wherever applicable — reusing the same shared OnnxModelBuilder::build()
that TensorRT now uses — while keeping this fork's existing raw .onnx loading and I/O name override
support for non-.bin.gz models.

Changes

  • cpp/neuralnet/onnxbackend.cpp: full rewrite onto the shared OnnxModelBuilder::build()
    architecture (matching PR Add ONNX Runtime backend support (OpenVINO / DirectML / TensorRT / CPU, Windows & Linux) #1222 / the TensorRT backend). Adds: scale8 FP16-range workaround
    (onnxSkipScale8, forced off for -contribute), per-thread OpenVINO device assignment
    (onnxOpenVINODeviceTypeThread<N>), per-device-type EP option overrides
    (onnxOpenVINODeviceConfig_<NPU|GPU|CPU>_<Option>), onnxOpenVINOPrecision/NumStreams/NumOfThreads/ ModelPriority, onnxTransformerNHWC, KATAGO_DUMP_ONNX debug dump, explicit rejection of the
    global useFP16 flag (provider controls precision instead), NeuralNet::setIsWarmup stub. Default
    OpenVINO device type kept as NPU (intentional deviation from PR Add ONNX Runtime backend support (OpenVINO / DirectML / TensorRT / CPU, Windows & Linux) #1222's GPU default, since this
    branch targets Intel NPU first). Raw .onnx loading updated to case-insensitive introspection and
    PascalCase default I/O names matching the new builder's actual output names.
  • cpp/neuralnet/onnxmodelbuilder.cpp/.h: cherry-picked PR Add ONNX Runtime backend support (OpenVINO / DirectML / TensorRT / CPU, Windows & Linux) #1222's two OpenVINO-EP fixes onto
    the shared builder (also used by TensorRT): RMSNorm Pow(x,2) instead of Mul(x,x) (OpenVINO's
    RMSFusion matcher requires the former, applied to all 4 RMSNorm call sites, not just the 2 PR Add ONNX Runtime backend support (OpenVINO / DirectML / TensorRT / CPU, Windows & Linux) #1222
    touched), and InputSpatial/InputGlobal/[InputMeta]/InputMask declaration order (OpenVINO EP
    misroutes the mask tensor with the old InputMask-first order).
  • cpp/command/misc.cpp: fixed exportonnx, which called the now-removed
    OnnxModelBuilder::buildOnnxModel.
  • cpp/program/setup.cpp: added the onnxSkipScale8 force-off guard for distributed selfplay.
  • cpp/neuralnet/nneval.h/.cpp, cpp/program/setup.cpp: new generic (backend-agnostic)
    per-server-thread max batch size override, nnMaxBatchSizeThread<N>, parallel to the existing
    gpuIdxByServerThread. Motivated by community feedback on upstream PR Add ONNX Runtime backend support (OpenVINO / DirectML / TensorRT / CPU, Windows & Linux) #1222 that NPU+iGPU hybrid
    setups need different optimal batch sizes per device (NPU ~1, iGPU larger) in addition to per-thread
    device assignment.
  • cpp/CMakeLists.txt: unified the ONNX backend's protobuf/onnx.proto dependency onto the same
    protobuf_generate_cpp mechanism the TensorRT backend uses (removes a redundant separate
    onnx-package vcpkg dependency); KATAGO_DEPS_DIR now uses CMAKE_BINARY_DIR instead of
    CMAKE_SOURCE_DIR/build (avoids polluting the source tree on out-of-tree builds); Windows vcpkg
    triplet default changed to x64-windows-static-md (avoids duplicate abseil_dll.dll instances /
    heap corruption).
  • cpp/configs/gtp_example.cfg, Compiling.md, README.md: documentation refresh for the new
    onnx*/nnMaxBatchSizeThread<N> keys and the .bin.gz-direct workflow (no .onnx export needed);
    added NPU-only / iGPU-only / NPU+iGPU-hybrid usage examples to the README's ONNX Quick Start
    sections (Windows + Linux).

Testing

  • cmake --build (MSVC, USE_BACKEND=ONNX) succeeds cleanly, no new warnings.
  • Verified end-to-end on real hardware (Intel NPU + iGPU) with a v1.17 transformer model
    (b11c768h12nbt3tflrs-fson-silu.bin.gz):
    • NPU-only and iGPU-only benchmark runs complete cleanly with sane outputs.
    • Hybrid config (numNNServerThreadsPerModel=2, one thread pinned to NPU, one to iGPU, with
      nnMaxBatchSizeThread0=1 / nnMaxBatchSizeThread1=8) runs both devices concurrently and
      confirms the new per-thread batch size actually takes effect (NPU thread processed rows 1:1 with
      batches, iGPU thread batched multiple rows per call).
    • Confirmed the OpenVINO-EP input declaration order fix and RMSNorm fix are exercised for real
      (transformerNHWC=true, transformer trunk model) without shape-mismatch errors.

@Looong01

Looong01 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Intel GPU test on Ultra 9 275HX:

(base) PS C:\Envs\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64> .\katago.exe benchmark -model b11c768h12nbt3tflrs-fson-silu.bin.gz -config gtp_npu.cfg -override-config "onnxOpenVINODeviceType=GPU"
2026-08-05 17:27:45+0800: Running with following config:
allowResignation = true
lagBuffer = 1.0
logAllGTPCommunication = true
logDir = gtp_logs
logSearchInfo = true
logSearchInfoForChosenMove = false
logToStderr = false
maxTimePondering = 60.0
maxVisits = 500
numSearchThreads = 1
onnxOpenVINODeviceType = GPU
onnxProvider = openvino
ponderingEnabled = false
resignConsecTurns = 3
resignThreshold = -0.90
rules = tromp-taylor
searchFactorAfterOnePass = 0.50
searchFactorAfterTwoPass = 0.25
searchFactorWhenWinning = 0.40
searchFactorWhenWinningThreshold = 0.95

2026-08-05 17:27:45+0800: Loading model and initializing benchmark...
2026-08-05 17:27:45+0800: Testing with default positions for board size: 19
2026-08-05 17:27:45+0800: nnRandSeed0 = 16002040592701319046
2026-08-05 17:27:45+0800: After dedups: nnModelFile0 = b11c768h12nbt3tflrs-fson-silu.bin.gz useFP16 auto
2026-08-05 17:27:45+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 17:27:47+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 17:27:47+0800: ONNX backend thread 0: Model version 17
2026-08-05 17:27:47+0800: ONNX backend thread 0: Model name: b11c768h12nbt3tflrs-fson-silu
2026-08-05 17:27:47+0800: ONNX backend thread 0: provider=openvino deviceIdx=GPU
2026-08-05 17:27:47+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 17:27:47+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 17:27:48+0800: ONNX backend: ONNX graph built (355300041 bytes)
2026-08-05 17:27:48+0800: ONNX backend: creating session...
2026-08-05 17:27:48+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=GPU
2026-08-05 17:27:56+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 17:27:56+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 17:27:56+0800: ONNX backend: session created, inputs=3 outputs=5

2026-08-05 17:28:12+0800: Loaded config gtp_npu.cfg and/or command-line and query overrides
2026-08-05 17:28:12+0800: Loaded model b11c768h12nbt3tflrs-fson-silu.bin.gz

Testing using 800 visits.
  If you have a good GPU, you might increase this using "-visits N" to get more accurate results.
  If you have a weak GPU and this is taking forever, you can decrease it instead to finish the benchmark faster.

You are currently using the ONNX Runtime version of KataGo.
Your GTP config is currently set to onnxProvider = openvino
OpenVINO device type = GPU
For Intel NPU, typically set onnxOpenVINODeviceType = NPU.
OpenVINO/NPU usually uses a single device; onnxDeviceToUseThread* is typically for cuda/trt/migraphx providers.

Your GTP config is currently set to use numSearchThreads = 1
Automatically trying different numbers of threads to home in on the best (board size 19x19):

2026-08-05 17:28:12+0800: GPU -1 finishing, processed 5 rows 5 batches
2026-08-05 17:28:12+0800: nnRandSeed0 = 15077079755957294086
2026-08-05 17:28:12+0800: After dedups: nnModelFile0 = b11c768h12nbt3tflrs-fson-silu.bin.gz useFP16 auto
2026-08-05 17:28:12+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 17:28:14+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 17:28:14+0800: ONNX backend thread 0: Model version 17
2026-08-05 17:28:14+0800: ONNX backend thread 0: Model name: b11c768h12nbt3tflrs-fson-silu
2026-08-05 17:28:14+0800: ONNX backend thread 0: provider=openvino deviceIdx=GPU
2026-08-05 17:28:14+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 17:28:14+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 17:28:15+0800: ONNX backend: ONNX graph built (355300041 bytes)
2026-08-05 17:28:15+0800: ONNX backend: creating session...
2026-08-05 17:28:15+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=GPU
2026-08-05 17:28:19+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 17:28:19+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 17:28:19+0800: ONNX backend: session created, inputs=3 outputs=5


Possible numbers of threads to test: 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32,

numSearchThreads =  5: 10 / 10 positions, visits/s = 9.99 nnEvals/s = 9.99 nnBatches/s = 4.01 avgBatchSize = 2.49 (804.3 secs)
numSearchThreads = 12: 10 / 10 positions, visits/s = 11.90 nnEvals/s = 11.10 nnBatches/s = 1.85 avgBatchSize = 6.01 (681.3 secs)
numSearchThreads =  3: 10 / 10 positions, visits/s = 9.37 nnEvals/s = 9.37 nnBatches/s = 6.25 avgBatchSize = 1.50 (856.0 secs)
numSearchThreads =  6: 10 / 10 positions, visits/s = 10.35 nnEvals/s = 10.31 nnBatches/s = 3.45 avgBatchSize = 2.99 (777.4 secs)
numSearchThreads =  8: 10 / 10 positions, visits/s = 11.09 nnEvals/s = 10.71 nnBatches/s = 2.69 avgBatchSize = 3.99 (727.6 secs)
numSearchThreads = 10: 10 / 10 positions, visits/s = 11.63 nnEvals/s = 11.26 nnBatches/s = 2.26 avgBatchSize = 4.98 (695.2 secs)


Ordered summary of results:

numSearchThreads =  3: 10 / 10 positions, visits/s = 9.37 nnEvals/s = 9.37 nnBatches/s = 6.25 avgBatchSize = 1.50 (856.0 secs) (EloDiff baseline)
numSearchThreads =  5: 10 / 10 positions, visits/s = 9.99 nnEvals/s = 9.99 nnBatches/s = 4.01 avgBatchSize = 2.49 (804.3 secs) (EloDiff -1)
numSearchThreads =  6: 10 / 10 positions, visits/s = 10.35 nnEvals/s = 10.31 nnBatches/s = 3.45 avgBatchSize = 2.99 (777.4 secs) (EloDiff +0)
numSearchThreads =  8: 10 / 10 positions, visits/s = 11.09 nnEvals/s = 10.71 nnBatches/s = 2.69 avgBatchSize = 3.99 (727.6 secs) (EloDiff +1)
numSearchThreads = 10: 10 / 10 positions, visits/s = 11.63 nnEvals/s = 11.26 nnBatches/s = 2.26 avgBatchSize = 4.98 (695.2 secs) (EloDiff -5)
numSearchThreads = 12: 10 / 10 positions, visits/s = 11.90 nnEvals/s = 11.10 nnBatches/s = 1.85 avgBatchSize = 6.01 (681.3 secs) (EloDiff -20)


Based on some test data, each speed doubling gains perhaps ~250 Elo by searching deeper.
Based on some test data, each thread costs perhaps 7 Elo if using 800 visits, and 2 Elo if using 5000 visits (by making MCTS worse).
So APPROXIMATELY based on this benchmark, if you intend to do a 5 second search:
numSearchThreads =  3: (baseline)
numSearchThreads =  5:    -1 Elo
numSearchThreads =  6:    +0 Elo
numSearchThreads =  8:    +1 Elo (recommended)
numSearchThreads = 10:    -5 Elo
numSearchThreads = 12:   -20 Elo

If you care about performance, you may want to edit numSearchThreads in gtp_npu.cfg and/or command-line and query overrides based on the above results!
If you intend to do much longer searches, configure the seconds per game move you expect with the '-time' flag and benchmark again.
If you intend to do short or fixed-visit searches, use lower numSearchThreads for better strength, high threads will weaken strength.
If interested see also other notes about performance and mem usage in the top of gtp_npu.cfg and/or command-line and query overrides

2026-08-05 18:44:05+0800: GPU -1 finishing, processed 47255 rows 16046 batches

@foxrainowo

foxrainowo commented Aug 5, 2026

Copy link
Copy Markdown

I'm having issues using gtp_npu.cfg. It stays at visits/s = -nan(ind) for about ten minutes, then reports an error and exits.

PS C:\Users\foxrain> C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\katago.exe benchmark -model "C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz" -config "C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_npu.cfg"
2026-08-05 19:56:55+0800: Running with following config:
allowResignation = true
lagBuffer = 1.0
logAllGTPCommunication = true
logDir = gtp_logs
logSearchInfo = true
logSearchInfoForChosenMove = false
logToStderr = false
maxTimePondering = 60.0
maxVisits = 500
numSearchThreads = 1
onnxOpenVINODeviceType = NPU
onnxProvider = openvino
ponderingEnabled = false
resignConsecTurns = 3
resignThreshold = -0.90
rules = tromp-taylor
searchFactorAfterOnePass = 0.50
searchFactorAfterTwoPass = 0.25
searchFactorWhenWinning = 0.40
searchFactorWhenWinningThreshold = 0.95

2026-08-05 19:56:55+0800: Loading model and initializing benchmark...
2026-08-05 19:56:55+0800: Testing with default positions for board size: 19
2026-08-05 19:56:55+0800: nnRandSeed0 = 6661592873850738289
2026-08-05 19:56:55+0800: After dedups: nnModelFile0 = C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz useFP16 auto
2026-08-05 19:56:55+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 19:57:04+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 19:57:04+0800: ONNX backend thread 0: Model version 15
2026-08-05 19:57:04+0800: ONNX backend thread 0: Model name: kata1-zhizi-b40c768nbt-fdx6d-s11272M-d5935M
2026-08-05 19:57:04+0800: ONNX backend thread 0: provider=openvino deviceIdx=NPU
2026-08-05 19:57:04+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 19:57:04+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 19:57:05+0800: ONNX backend: ONNX graph built (930658630 bytes)
2026-08-05 19:57:05+0800: ONNX backend: creating session...
2026-08-05 19:57:05+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=NPU
2026-08-05 19:57:08+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 19:57:08+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 19:57:08+0800: ONNX backend: session created, inputs=3 outputs=5

2026-08-05 19:57:11+0800: Loaded config C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_npu.cfg
2026-08-05 19:57:11+0800: Loaded model C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz

Testing using 800 visits.
  If you have a good GPU, you might increase this using "-visits N" to get more accurate results.
  If you have a weak GPU and this is taking forever, you can decrease it instead to finish the benchmark faster.

You are currently using the ONNX Runtime version of KataGo.
Your GTP config is currently set to onnxProvider = openvino
OpenVINO device type = NPU
For Intel NPU, typically set onnxOpenVINODeviceType = NPU.
OpenVINO/NPU usually uses a single device; onnxDeviceToUseThread* is typically for cuda/trt/migraphx providers.

Your GTP config is currently set to use numSearchThreads = 1
Automatically trying different numbers of threads to home in on the best (board size 19x19):

2026-08-05 19:57:11+0800: GPU -1 finishing, processed 5 rows 5 batches
2026-08-05 19:57:11+0800: nnRandSeed0 = 9916478682849984213
2026-08-05 19:57:11+0800: After dedups: nnModelFile0 = C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz useFP16 auto
2026-08-05 19:57:11+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 19:57:20+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 19:57:20+0800: ONNX backend thread 0: Model version 15
2026-08-05 19:57:20+0800: ONNX backend thread 0: Model name: kata1-zhizi-b40c768nbt-fdx6d-s11272M-d5935M
2026-08-05 19:57:20+0800: ONNX backend thread 0: provider=openvino deviceIdx=NPU
2026-08-05 19:57:20+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 19:57:20+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 19:57:21+0800: ONNX backend: ONNX graph built (930658630 bytes)
2026-08-05 19:57:22+0800: ONNX backend: creating session...
2026-08-05 19:57:22+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=NPU
2026-08-05 19:57:24+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 19:57:24+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 19:57:24+0800: ONNX backend: session created, inputs=3 outputs=5


Possible numbers of threads to test: 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32,

numSearchThreads =  5: 1 / 10 positions, visits/s = 1.31 (616.0 secs)      2026-08-05 20:07:58.5189468 [E:onnxruntime:, sequential_executor.cc:671 onnxruntime::ExecuteKernel] Non-zero status code returned while running OpenVINO-EP-subgraph_1 node. Name:'OpenVINOExecutionProvider_OpenVINO-EP-subgraph_1_0' Status Message: C:\Users\Loong\Codes\onnxruntime\onnxruntime\core\providers\openvino\ov_interface.cc:28 void __cdecl onnxruntime::openvino_ep::OvExceptionBoundary<false,class onnxruntime::openvino_ep::OVInferRequest::Infer::<lambda_1>,>(class onnxruntime::openvino_ep::OVInferRequest::Infer::<lambda_1> &&,struct std::basic_format_string<char> &&) [OpenVINO-EP] In Error Couldn't start Inference: Exception from src\inference\src\cpp\infer_request.cpp:224:
Exception from src\plugins\intel_npu\src\utils\src\zero\zero_wrappers.cpp:354:
L0 zeCommandQueueExecuteCommandLists result: ZE_RESULT_ERROR_DEVICE_LOST, code 0x70000001 - device hung, reset, was removed, or driver update occurred

PS C:\Users\foxrain>

Although gtp_npu_gpu_hybrid and gtp_gpu.cfg work normally, I found that their performance is gtp_gpu.cfg >= gtp_npu_gpu_hybrid, meaning the hybrid configuration is actually slower, so it's better to just use the GPU.
Additionally, it did not generate the compilation cache at the specified location in gtp. But it will generate in match.

PS C:\Users\foxrain> C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\katago.exe benchmark -model "C:\Users\foxrain\Downloads\b11c768h12nbt3tflrs-fson-silu.bin.gz" -config "C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_npu_gpu_hybrid.cfg"
2026-08-05 18:24:33+0800: Running with following config:
allowResignation = true
lagBuffer = 1.0
logAllGTPCommunication = true
logDir = gtp_logs
logSearchInfo = true
logSearchInfoForChosenMove = false
logToStderr = false
maxTimePondering = 60.0
maxVisits = 500
nnMaxBatchSizeThread0 = 1
nnMaxBatchSizeThread1 = 8
numNNServerThreadsPerModel = 2
numSearchThreads = 1
onnxOpenVINODeviceType = NPU
onnxOpenVINODeviceTypeThread0 = NPU
onnxOpenVINODeviceTypeThread1 = GPU
onnxProvider = openvino
ponderingEnabled = false
resignConsecTurns = 3
resignThreshold = -0.90
rules = japanese
searchFactorAfterOnePass = 0.50
searchFactorAfterTwoPass = 0.25
searchFactorWhenWinning = 0.40
searchFactorWhenWinningThreshold = 0.95

2026-08-05 18:24:33+0800: Loading model and initializing benchmark...
2026-08-05 18:24:33+0800: Testing with default positions for board size: 19
2026-08-05 18:24:33+0800: nnRandSeed0 = 6992435971789407022
2026-08-05 18:24:33+0800: After dedups: nnModelFile0 = C:\Users\foxrain\Downloads\b11c768h12nbt3tflrs-fson-silu.bin.gz useFP16 auto
2026-08-05 18:24:33+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 18:24:36+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 18:24:36+0800: ONNX backend thread 0: Model version 17
2026-08-05 18:24:36+0800: ONNX backend thread 0: Model name: b11c768h12nbt3tflrs-fson-silu
2026-08-05 18:24:36+0800: ONNX backend thread 0: provider=openvino deviceIdx=NPU
2026-08-05 18:24:36+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 18:24:36+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 18:24:36+0800: ONNX backend thread 1: Model version 17
2026-08-05 18:24:36+0800: ONNX backend thread 1: Model name: b11c768h12nbt3tflrs-fson-silu
2026-08-05 18:24:36+0800: ONNX backend thread 1: provider=openvino deviceIdx=GPU
2026-08-05 18:24:36+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 18:24:36+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 18:24:37+0800: ONNX backend: ONNX graph built (355300041 bytes)
2026-08-05 18:24:37+0800: ONNX backend: ONNX graph built (355300041 bytes)
2026-08-05 18:24:37+0800: ONNX backend: creating session...
2026-08-05 18:24:37+0800: ONNX backend: creating session...
2026-08-05 18:24:40+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=NPU
2026-08-05 18:24:40+0800: ONNX backend: OpenVINO EP enabled for thread 1, device_type=GPU
2026-08-05 18:24:41+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 18:24:41+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 18:24:41+0800: ONNX backend: session created, inputs=3 outputs=5
2026-08-05 18:24:45+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 18:24:45+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 18:24:45+0800: ONNX backend: session created, inputs=3 outputs=5

2026-08-05 18:24:48+0800: Loaded config C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_npu_gpu_hybrid.cfg
2026-08-05 18:24:48+0800: Loaded model C:\Users\foxrain\Downloads\b11c768h12nbt3tflrs-fson-silu.bin.gz

Testing using 800 visits.
  If you have a good GPU, you might increase this using "-visits N" to get more accurate results.
  If you have a weak GPU and this is taking forever, you can decrease it instead to finish the benchmark faster.

You are currently using the ONNX Runtime version of KataGo.
Your GTP config is currently set to onnxProvider = openvino
OpenVINO device type = NPU
For Intel NPU, typically set onnxOpenVINODeviceType = NPU.
OpenVINO/NPU usually uses a single device; onnxDeviceToUseThread* is typically for cuda/trt/migraphx providers.

Your GTP config is currently set to use numSearchThreads = 1
Automatically trying different numbers of threads to home in on the best (board size 19x19):

2026-08-05 18:24:48+0800: GPU -1 finishing, processed 1 rows 1 batches
2026-08-05 18:24:48+0800: GPU -1 finishing, processed 4 rows 4 batches
2026-08-05 18:24:48+0800: nnRandSeed0 = 135529463997515913
2026-08-05 18:24:48+0800: After dedups: nnModelFile0 = C:\Users\foxrain\Downloads\b11c768h12nbt3tflrs-fson-silu.bin.gz useFP16 auto
2026-08-05 18:24:48+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 18:24:51+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 18:24:51+0800: ONNX backend thread 0: Model version 17
2026-08-05 18:24:51+0800: ONNX backend thread 0: Model name: b11c768h12nbt3tflrs-fson-silu
2026-08-05 18:24:51+0800: ONNX backend thread 0: provider=openvino deviceIdx=NPU
2026-08-05 18:24:51+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 18:24:51+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 18:24:51+0800: ONNX backend thread 1: Model version 17
2026-08-05 18:24:51+0800: ONNX backend thread 1: Model name: b11c768h12nbt3tflrs-fson-silu
2026-08-05 18:24:51+0800: ONNX backend thread 1: provider=openvino deviceIdx=GPU
2026-08-05 18:24:51+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 18:24:51+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 18:24:51+0800: ONNX backend: ONNX graph built (355300041 bytes)
2026-08-05 18:24:51+0800: ONNX backend: ONNX graph built (355300041 bytes)
2026-08-05 18:24:51+0800: ONNX backend: creating session...
2026-08-05 18:24:51+0800: ONNX backend: creating session...
2026-08-05 18:24:51+0800: ONNX backend: OpenVINO EP enabled for thread 1, device_type=GPU
2026-08-05 18:24:51+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=NPU
2026-08-05 18:24:52+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 18:24:52+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 18:24:52+0800: ONNX backend: session created, inputs=3 outputs=5
2026-08-05 18:24:55+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 18:24:55+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 18:24:55+0800: ONNX backend: session created, inputs=3 outputs=5


Possible numbers of threads to test: 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32,

numSearchThreads =  5: 10 / 10 positions, visits/s = 51.07 nnEvals/s = 44.07 nnBatches/s = 23.10 avgBatchSize = 1.91 (157.4 secs)
numSearchThreads = 12: 10 / 10 positions, visits/s = 66.09 nnEvals/s = 57.16 nnBatches/s = 11.08 avgBatchSize = 5.16 (122.7 secs)
numSearchThreads = 10: 10 / 10 positions, visits/s = 68.09 nnEvals/s = 58.36 nnBatches/s = 12.06 avgBatchSize = 4.84 (118.8 secs)
numSearchThreads = 20: 10 / 10 positions, visits/s = 72.20 nnEvals/s = 64.16 nnBatches/s = 11.81 avgBatchSize = 5.43 (113.4 secs)
numSearchThreads =  8: 10 / 10 positions, visits/s = 65.25 nnEvals/s = 54.81 nnBatches/s = 13.95 avgBatchSize = 3.93 (123.7 secs)
numSearchThreads =  6: 10 / 10 positions, visits/s = 61.63 nnEvals/s = 52.07 nnBatches/s = 18.39 avgBatchSize = 2.83 (130.6 secs)


Ordered summary of results:

numSearchThreads =  5: 10 / 10 positions, visits/s = 51.07 nnEvals/s = 44.07 nnBatches/s = 23.10 avgBatchSize = 1.91 (157.4 secs) (EloDiff baseline)
numSearchThreads =  6: 10 / 10 positions, visits/s = 61.63 nnEvals/s = 52.07 nnBatches/s = 18.39 avgBatchSize = 2.83 (130.6 secs) (EloDiff +60)
numSearchThreads =  8: 10 / 10 positions, visits/s = 65.25 nnEvals/s = 54.81 nnBatches/s = 13.95 avgBatchSize = 3.93 (123.7 secs) (EloDiff +63)
numSearchThreads = 10: 10 / 10 positions, visits/s = 68.09 nnEvals/s = 58.36 nnBatches/s = 12.06 avgBatchSize = 4.84 (118.8 secs) (EloDiff +60)
numSearchThreads = 12: 10 / 10 positions, visits/s = 66.09 nnEvals/s = 57.16 nnBatches/s = 11.08 avgBatchSize = 5.16 (122.7 secs) (EloDiff +30)
numSearchThreads = 20: 10 / 10 positions, visits/s = 72.20 nnEvals/s = 64.16 nnBatches/s = 11.81 avgBatchSize = 5.43 (113.4 secs) (EloDiff -9)


Based on some test data, each speed doubling gains perhaps ~250 Elo by searching deeper.
Based on some test data, each thread costs perhaps 7 Elo if using 800 visits, and 2 Elo if using 5000 visits (by making MCTS worse).
So APPROXIMATELY based on this benchmark, if you intend to do a 5 second search:
numSearchThreads =  5: (baseline)
numSearchThreads =  6:   +60 Elo
numSearchThreads =  8:   +63 Elo (recommended)
numSearchThreads = 10:   +60 Elo
numSearchThreads = 12:   +30 Elo
numSearchThreads = 20:    -9 Elo

If you care about performance, you may want to edit numSearchThreads in C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_npu_gpu_hybrid.cfg based on the above results!
If you intend to do much longer searches, configure the seconds per game move you expect with the '-time' flag and benchmark again.
If you intend to do short or fixed-visit searches, use lower numSearchThreads for better strength, high threads will weaken strength.
If interested see also other notes about performance and mem usage in the top of C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_npu_gpu_hybrid.cfg

2026-08-05 18:37:48+0800: GPU -1 finishing, processed 38472 rows 8626 batches
2026-08-05 18:37:48+0800: GPU -1 finishing, processed 3276 rows 3276 batches
PS C:\Users\foxrain> C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\katago.exe benchmark -model "C:\Users\foxrain\Downloads\b11c768h12nbt3tflrs-fson-silu.bin.gz" -config "C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_gpu.cfg"
2026-08-05 20:46:55+0800: Running with following config:
allowResignation = true
lagBuffer = 1.0
logAllGTPCommunication = true
logDir = gtp_logs
logSearchInfo = true
logSearchInfoForChosenMove = false
logToStderr = false
maxTimePondering = 60.0
maxVisits = 500
numSearchThreads = 4
onnxOpenVINODeviceType = GPU
onnxProvider = openvino
ponderingEnabled = false
resignConsecTurns = 3
resignThreshold = -0.90
rules = tromp-taylor
searchFactorAfterOnePass = 0.50
searchFactorAfterTwoPass = 0.25
searchFactorWhenWinning = 0.40
searchFactorWhenWinningThreshold = 0.95

2026-08-05 20:46:55+0800: Loading model and initializing benchmark...
2026-08-05 20:46:55+0800: Testing with default positions for board size: 19
2026-08-05 20:46:55+0800: nnRandSeed0 = 7983874965468556636
2026-08-05 20:46:55+0800: After dedups: nnModelFile0 = C:\Users\foxrain\Downloads\b11c768h12nbt3tflrs-fson-silu.bin.gz useFP16 auto
2026-08-05 20:46:55+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 20:46:57+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 20:46:57+0800: ONNX backend thread 0: Model version 17
2026-08-05 20:46:57+0800: ONNX backend thread 0: Model name: b11c768h12nbt3tflrs-fson-silu
2026-08-05 20:46:57+0800: ONNX backend thread 0: provider=openvino deviceIdx=GPU
2026-08-05 20:46:57+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 20:46:57+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 20:46:58+0800: ONNX backend: ONNX graph built (355300041 bytes)
2026-08-05 20:46:58+0800: ONNX backend: creating session...
2026-08-05 20:46:58+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=GPU
2026-08-05 20:47:01+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 20:47:01+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 20:47:01+0800: ONNX backend: session created, inputs=3 outputs=5

2026-08-05 20:47:02+0800: Loaded config C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_gpu.cfg
2026-08-05 20:47:02+0800: Loaded model C:\Users\foxrain\Downloads\b11c768h12nbt3tflrs-fson-silu.bin.gz

Testing using 800 visits.
  If you have a good GPU, you might increase this using "-visits N" to get more accurate results.
  If you have a weak GPU and this is taking forever, you can decrease it instead to finish the benchmark faster.

You are currently using the ONNX Runtime version of KataGo.
Your GTP config is currently set to onnxProvider = openvino
OpenVINO device type = GPU
For Intel NPU, typically set onnxOpenVINODeviceType = NPU.
OpenVINO/NPU usually uses a single device; onnxDeviceToUseThread* is typically for cuda/trt/migraphx providers.

Your GTP config is currently set to use numSearchThreads = 4
Automatically trying different numbers of threads to home in on the best (board size 19x19):

2026-08-05 20:47:02+0800: GPU -1 finishing, processed 5 rows 5 batches
2026-08-05 20:47:02+0800: nnRandSeed0 = 12156296877601320369
2026-08-05 20:47:02+0800: After dedups: nnModelFile0 = C:\Users\foxrain\Downloads\b11c768h12nbt3tflrs-fson-silu.bin.gz useFP16 auto
2026-08-05 20:47:02+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 20:47:09+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 20:47:09+0800: ONNX backend thread 0: Model version 17
2026-08-05 20:47:09+0800: ONNX backend thread 0: Model name: b11c768h12nbt3tflrs-fson-silu
2026-08-05 20:47:09+0800: ONNX backend thread 0: provider=openvino deviceIdx=GPU
2026-08-05 20:47:09+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 20:47:09+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 20:47:10+0800: ONNX backend: ONNX graph built (355300041 bytes)
2026-08-05 20:47:10+0800: ONNX backend: creating session...
2026-08-05 20:47:10+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=GPU
2026-08-05 20:47:13+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 20:47:13+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 20:47:13+0800: ONNX backend: session created, inputs=3 outputs=5


Possible numbers of threads to test: 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32,

numSearchThreads =  5: 10 / 10 positions, visits/s = 80.77 nnEvals/s = 68.57 nnBatches/s = 17.55 avgBatchSize = 3.91 (99.5 secs)
numSearchThreads = 12: 10 / 10 positions, visits/s = 75.99 nnEvals/s = 65.56 nnBatches/s = 6.15 avgBatchSize = 10.66 (106.7 secs)
numSearchThreads =  3: 10 / 10 positions, visits/s = 61.40 nnEvals/s = 51.35 nnBatches/s = 25.86 avgBatchSize = 1.99 (130.6 secs)
numSearchThreads =  6: 10 / 10 positions, visits/s = 65.82 nnEvals/s = 56.23 nnBatches/s = 11.98 avgBatchSize = 4.69 (122.3 secs)
numSearchThreads =  2: 10 / 10 positions, visits/s = 29.03 nnEvals/s = 24.63 nnBatches/s = 22.49 avgBatchSize = 1.10 (276.0 secs)
numSearchThreads =  4: 10 / 10 positions, visits/s = 64.18 nnEvals/s = 54.39 nnBatches/s = 19.21 avgBatchSize = 2.83 (125.1 secs)


Ordered summary of results:

numSearchThreads =  2: 10 / 10 positions, visits/s = 29.03 nnEvals/s = 24.63 nnBatches/s = 22.49 avgBatchSize = 1.10 (276.0 secs) (EloDiff baseline)
numSearchThreads =  3: 10 / 10 positions, visits/s = 61.40 nnEvals/s = 51.35 nnBatches/s = 25.86 avgBatchSize = 1.99 (130.6 secs) (EloDiff +263)
numSearchThreads =  4: 10 / 10 positions, visits/s = 64.18 nnEvals/s = 54.39 nnBatches/s = 19.21 avgBatchSize = 2.83 (125.1 secs) (EloDiff +270)
numSearchThreads =  5: 10 / 10 positions, visits/s = 80.77 nnEvals/s = 68.57 nnBatches/s = 17.55 avgBatchSize = 3.91 (99.5 secs) (EloDiff +346)
numSearchThreads =  6: 10 / 10 positions, visits/s = 65.82 nnEvals/s = 56.23 nnBatches/s = 11.98 avgBatchSize = 4.69 (122.3 secs) (EloDiff +261)
numSearchThreads = 12: 10 / 10 positions, visits/s = 75.99 nnEvals/s = 65.56 nnBatches/s = 6.15 avgBatchSize = 10.66 (106.7 secs) (EloDiff +260)


Based on some test data, each speed doubling gains perhaps ~250 Elo by searching deeper.
Based on some test data, each thread costs perhaps 7 Elo if using 800 visits, and 2 Elo if using 5000 visits (by making MCTS worse).
So APPROXIMATELY based on this benchmark, if you intend to do a 5 second search:
numSearchThreads =  2: (baseline)
numSearchThreads =  3:  +263 Elo
numSearchThreads =  4:  +270 Elo
numSearchThreads =  5:  +346 Elo (recommended)
numSearchThreads =  6:  +261 Elo
numSearchThreads = 12:  +260 Elo

If you care about performance, you may want to edit numSearchThreads in C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_gpu.cfg based on the above results!
If you intend to do much longer searches, configure the seconds per game move you expect with the '-time' flag and benchmark again.
If you intend to do short or fixed-visit searches, use lower numSearchThreads for better strength, high threads will weaken strength.
If interested see also other notes about performance and mem usage in the top of C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_gpu.cfg

2026-08-05 21:01:36+0800: GPU -1 finishing, processed 41013 rows 15861 batches
PS C:\Users\foxrain> C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\katago.exe benchmark -model "C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz" -config "C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_npu_gpu_hybrid.cfg"
2026-08-05 18:46:11+0800: Running with following config:
allowResignation = true
lagBuffer = 1.0
logAllGTPCommunication = true
logDir = gtp_logs
logSearchInfo = true
logSearchInfoForChosenMove = false
logToStderr = false
maxTimePondering = 60.0
maxVisits = 500
nnMaxBatchSizeThread0 = 1
nnMaxBatchSizeThread1 = 8
numNNServerThreadsPerModel = 2
numSearchThreads = 6
onnxOpenVINODeviceType = NPU
onnxOpenVINODeviceTypeThread0 = NPU
onnxOpenVINODeviceTypeThread1 = GPU
onnxProvider = openvino
ponderingEnabled = false
resignConsecTurns = 3
resignThreshold = -0.90
rules = japanese
searchFactorAfterOnePass = 0.50
searchFactorAfterTwoPass = 0.25
searchFactorWhenWinning = 0.40
searchFactorWhenWinningThreshold = 0.95

2026-08-05 18:46:11+0800: Loading model and initializing benchmark...
2026-08-05 18:46:11+0800: Testing with default positions for board size: 19
2026-08-05 18:46:11+0800: nnRandSeed0 = 10456369991387597996
2026-08-05 18:46:11+0800: After dedups: nnModelFile0 = C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz useFP16 auto
2026-08-05 18:46:11+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 18:46:40+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 18:46:40+0800: ONNX backend thread 0: Model version 15
2026-08-05 18:46:40+0800: ONNX backend thread 0: Model name: kata1-zhizi-b40c768nbt-fdx6d-s11272M-d5935M
2026-08-05 18:46:40+0800: ONNX backend thread 1: Model version 15
2026-08-05 18:46:40+0800: ONNX backend thread 1: Model name: kata1-zhizi-b40c768nbt-fdx6d-s11272M-d5935M
2026-08-05 18:46:40+0800: ONNX backend thread 0: provider=openvino deviceIdx=NPU
2026-08-05 18:46:40+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 18:46:40+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 18:46:40+0800: ONNX backend thread 1: provider=openvino deviceIdx=GPU
2026-08-05 18:46:40+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 18:46:40+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 18:46:42+0800: ONNX backend: ONNX graph built (930658630 bytes)
2026-08-05 18:46:42+0800: ONNX backend: ONNX graph built (930658630 bytes)
2026-08-05 18:46:42+0800: ONNX backend: creating session...
2026-08-05 18:46:42+0800: ONNX backend: creating session...
2026-08-05 18:46:42+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=NPU
2026-08-05 18:46:42+0800: ONNX backend: OpenVINO EP enabled for thread 1, device_type=GPU
2026-08-05 18:46:44+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 18:46:44+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 18:46:44+0800: ONNX backend: session created, inputs=3 outputs=5
2026-08-05 18:46:47+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 18:46:47+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 18:46:47+0800: ONNX backend: session created, inputs=3 outputs=5

2026-08-05 18:46:57+0800: Loaded config C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_npu_gpu_hybrid.cfg
2026-08-05 18:46:57+0800: Loaded model C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz

Testing using 800 visits.
  If you have a good GPU, you might increase this using "-visits N" to get more accurate results.
  If you have a weak GPU and this is taking forever, you can decrease it instead to finish the benchmark faster.

You are currently using the ONNX Runtime version of KataGo.
Your GTP config is currently set to onnxProvider = openvino
OpenVINO device type = NPU
For Intel NPU, typically set onnxOpenVINODeviceType = NPU.
OpenVINO/NPU usually uses a single device; onnxDeviceToUseThread* is typically for cuda/trt/migraphx providers.

Your GTP config is currently set to use numSearchThreads = 6
Automatically trying different numbers of threads to home in on the best (board size 19x19):

2026-08-05 18:46:57+0800: GPU -1 finishing, processed 2 rows 2 batches
2026-08-05 18:46:57+0800: GPU -1 finishing, processed 3 rows 3 batches
2026-08-05 18:46:57+0800: nnRandSeed0 = 3859048696684069015
2026-08-05 18:46:57+0800: After dedups: nnModelFile0 = C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz useFP16 auto
2026-08-05 18:46:57+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 18:47:15+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 18:47:15+0800: ONNX backend thread 1: Model version 15
2026-08-05 18:47:15+0800: ONNX backend thread 1: Model name: kata1-zhizi-b40c768nbt-fdx6d-s11272M-d5935M
2026-08-05 18:47:15+0800: ONNX backend thread 1: provider=openvino deviceIdx=GPU
2026-08-05 18:47:15+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 18:47:15+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 18:47:15+0800: ONNX backend thread 0: Model version 15
2026-08-05 18:47:15+0800: ONNX backend thread 0: Model name: kata1-zhizi-b40c768nbt-fdx6d-s11272M-d5935M
2026-08-05 18:47:15+0800: ONNX backend thread 0: provider=openvino deviceIdx=NPU
2026-08-05 18:47:15+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 18:47:15+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 18:47:16+0800: ONNX backend: ONNX graph built (930658630 bytes)
2026-08-05 18:47:16+0800: ONNX backend: ONNX graph built (930658630 bytes)
2026-08-05 18:47:16+0800: ONNX backend: creating session...
2026-08-05 18:47:16+0800: ONNX backend: OpenVINO EP enabled for thread 1, device_type=GPU
2026-08-05 18:47:16+0800: ONNX backend: creating session...
2026-08-05 18:47:16+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=NPU
2026-08-05 18:47:19+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 18:47:19+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 18:47:19+0800: ONNX backend: session created, inputs=3 outputs=5
2026-08-05 18:47:22+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 18:47:22+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 18:47:22+0800: ONNX backend: session created, inputs=3 outputs=5


Possible numbers of threads to test: 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32,

numSearchThreads =  5: 10 / 10 positions, visits/s = 23.42 nnEvals/s = 22.33 nnBatches/s = 11.58 avgBatchSize = 1.93 (343.2 secs)
numSearchThreads = 12: 10 / 10 positions, visits/s = 21.99 nnEvals/s = 21.67 nnBatches/s = 7.41 avgBatchSize = 2.92 (368.8 secs)
numSearchThreads =  3: 10 / 10 positions, visits/s = 22.52 nnEvals/s = 20.84 nnBatches/s = 20.43 avgBatchSize = 1.02 (356.2 secs)
numSearchThreads =  6: 10 / 10 positions, visits/s = 26.09 nnEvals/s = 24.67 nnBatches/s = 10.16 avgBatchSize = 2.43 (308.5 secs)
numSearchThreads =  8: 10 / 10 positions, visits/s = 26.15 nnEvals/s = 25.83 nnBatches/s = 8.72 avgBatchSize = 2.96 (308.6 secs)
numSearchThreads = 10: 10 / 10 positions, visits/s = 25.63 nnEvals/s = 25.14 nnBatches/s = 7.77 avgBatchSize = 3.23 (315.6 secs)


Ordered summary of results:

numSearchThreads =  3: 10 / 10 positions, visits/s = 22.52 nnEvals/s = 20.84 nnBatches/s = 20.43 avgBatchSize = 1.02 (356.2 secs) (EloDiff baseline)
numSearchThreads =  5: 10 / 10 positions, visits/s = 23.42 nnEvals/s = 22.33 nnBatches/s = 11.58 avgBatchSize = 1.93 (343.2 secs) (EloDiff -8)
numSearchThreads =  6: 10 / 10 positions, visits/s = 26.09 nnEvals/s = 24.67 nnBatches/s = 10.16 avgBatchSize = 2.43 (308.5 secs) (EloDiff +20)
numSearchThreads =  8: 10 / 10 positions, visits/s = 26.15 nnEvals/s = 25.83 nnBatches/s = 8.72 avgBatchSize = 2.96 (308.6 secs) (EloDiff -1)
numSearchThreads = 10: 10 / 10 positions, visits/s = 25.63 nnEvals/s = 25.14 nnBatches/s = 7.77 avgBatchSize = 3.23 (315.6 secs) (EloDiff -31)
numSearchThreads = 12: 10 / 10 positions, visits/s = 21.99 nnEvals/s = 21.67 nnBatches/s = 7.41 avgBatchSize = 2.92 (368.8 secs) (EloDiff -110)


Based on some test data, each speed doubling gains perhaps ~250 Elo by searching deeper.
Based on some test data, each thread costs perhaps 7 Elo if using 800 visits, and 2 Elo if using 5000 visits (by making MCTS worse).
So APPROXIMATELY based on this benchmark, if you intend to do a 5 second search:
numSearchThreads =  3: (baseline)
numSearchThreads =  5:    -8 Elo
numSearchThreads =  6:   +20 Elo (recommended)
numSearchThreads =  8:    -1 Elo
numSearchThreads = 10:   -31 Elo
numSearchThreads = 12:  -110 Elo

If you care about performance, you may want to edit numSearchThreads in C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_npu_gpu_hybrid.cfg based on the above results!
If you intend to do much longer searches, configure the seconds per game move you expect with the '-time' flag and benchmark again.
If you intend to do short or fixed-visit searches, use lower numSearchThreads for better strength, high threads will weaken strength.
If interested see also other notes about performance and mem usage in the top of C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_npu_gpu_hybrid.cfg

2026-08-05 19:20:56+0800: GPU -1 finishing, processed 36059 rows 11731 batches
2026-08-05 19:20:56+0800: GPU -1 finishing, processed 10539 rows 10539 batches
PS C:\Users\foxrain> C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\katago.exe benchmark -model "C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz" -config "C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_gpu.cfg"
2026-08-05 20:08:58+0800: Running with following config:
allowResignation = true
lagBuffer = 1.0
logAllGTPCommunication = true
logDir = gtp_logs
logSearchInfo = true
logSearchInfoForChosenMove = false
logToStderr = false
maxTimePondering = 60.0
maxVisits = 500
numSearchThreads = 4
onnxOpenVINODeviceType = GPU
onnxProvider = openvino
ponderingEnabled = false
resignConsecTurns = 3
resignThreshold = -0.90
rules = tromp-taylor
searchFactorAfterOnePass = 0.50
searchFactorAfterTwoPass = 0.25
searchFactorWhenWinning = 0.40
searchFactorWhenWinningThreshold = 0.95

2026-08-05 20:08:58+0800: Loading model and initializing benchmark...
2026-08-05 20:08:58+0800: Testing with default positions for board size: 19
2026-08-05 20:08:58+0800: nnRandSeed0 = 14922272731934780901
2026-08-05 20:08:58+0800: After dedups: nnModelFile0 = C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz useFP16 auto
2026-08-05 20:08:58+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 20:09:06+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 20:09:06+0800: ONNX backend thread 0: Model version 15
2026-08-05 20:09:06+0800: ONNX backend thread 0: Model name: kata1-zhizi-b40c768nbt-fdx6d-s11272M-d5935M
2026-08-05 20:09:06+0800: ONNX backend thread 0: provider=openvino deviceIdx=GPU
2026-08-05 20:09:06+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 20:09:06+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 20:09:07+0800: ONNX backend: ONNX graph built (930658630 bytes)
2026-08-05 20:09:07+0800: ONNX backend: creating session...
2026-08-05 20:09:07+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=GPU
2026-08-05 20:09:13+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 20:09:13+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 20:09:13+0800: ONNX backend: session created, inputs=3 outputs=5

2026-08-05 20:09:16+0800: Loaded config C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_gpu.cfg
2026-08-05 20:09:16+0800: Loaded model C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz

Testing using 800 visits.
  If you have a good GPU, you might increase this using "-visits N" to get more accurate results.
  If you have a weak GPU and this is taking forever, you can decrease it instead to finish the benchmark faster.

You are currently using the ONNX Runtime version of KataGo.
Your GTP config is currently set to onnxProvider = openvino
OpenVINO device type = GPU
For Intel NPU, typically set onnxOpenVINODeviceType = NPU.
OpenVINO/NPU usually uses a single device; onnxDeviceToUseThread* is typically for cuda/trt/migraphx providers.

Your GTP config is currently set to use numSearchThreads = 4
Automatically trying different numbers of threads to home in on the best (board size 19x19):

2026-08-05 20:09:16+0800: GPU -1 finishing, processed 5 rows 5 batches
2026-08-05 20:09:16+0800: nnRandSeed0 = 4338244724291888651
2026-08-05 20:09:16+0800: After dedups: nnModelFile0 = C:\Users\foxrain\Downloads\kata1-zhizi-b40c768nbt-s11272M-d5935M.bin.gz useFP16 auto
2026-08-05 20:09:16+0800: Initializing neural net buffer to be size 19 * 19 exactly
2026-08-05 20:09:34+0800: ONNX backend: creating compute context for 19x19 with provider 'openvino'
2026-08-05 20:09:34+0800: ONNX backend thread 0: Model version 15
2026-08-05 20:09:34+0800: ONNX backend thread 0: Model name: kata1-zhizi-b40c768nbt-fdx6d-s11272M-d5935M
2026-08-05 20:09:34+0800: ONNX backend thread 0: provider=openvino deviceIdx=GPU
2026-08-05 20:09:34+0800: ONNX backend: building ONNX graph from model weights...
2026-08-05 20:09:34+0800: Building internal onnx model, requireExactNNLen=false transformerNHWC=true
2026-08-05 20:09:37+0800: ONNX backend: ONNX graph built (930658630 bytes)
2026-08-05 20:09:37+0800: ONNX backend: creating session...
2026-08-05 20:09:38+0800: ONNX backend: OpenVINO EP enabled for thread 0, device_type=GPU
2026-08-05 20:09:53+0800: ONNX backend: graph input order: [0]InputSpatial [1]InputGlobal [2]InputMask
2026-08-05 20:09:53+0800: ONNX backend: graph output order: [0]OutputPolicyPass [1]OutputPolicy [2]OutputValue [3]OutputScoreValue [4]OutputOwnership
2026-08-05 20:09:53+0800: ONNX backend: session created, inputs=3 outputs=5


Possible numbers of threads to test: 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32,

numSearchThreads =  5: 10 / 10 positions, visits/s = 22.60 nnEvals/s = 20.75 nnBatches/s = 5.57 avgBatchSize = 3.73 (355.8 secs)
numSearchThreads = 12: 10 / 10 positions, visits/s = 19.97 nnEvals/s = 19.65 nnBatches/s = 1.84 avgBatchSize = 10.67 (406.1 secs)
numSearchThreads =  3: 10 / 10 positions, visits/s = 19.09 nnEvals/s = 17.57 nnBatches/s = 10.07 avgBatchSize = 1.74 (420.2 secs)
numSearchThreads =  6: 10 / 10 positions, visits/s = 24.76 nnEvals/s = 23.35 nnBatches/s = 4.91 avgBatchSize = 4.75 (325.1 secs)
numSearchThreads =  8: 10 / 10 positions, visits/s = 24.67 nnEvals/s = 23.53 nnBatches/s = 3.45 avgBatchSize = 6.82 (327.0 secs)
numSearchThreads =  4: 10 / 10 positions, visits/s = 26.62 nnEvals/s = 24.16 nnBatches/s = 8.23 avgBatchSize = 2.94 (301.7 secs)


Ordered summary of results:

numSearchThreads =  3: 10 / 10 positions, visits/s = 19.09 nnEvals/s = 17.57 nnBatches/s = 10.07 avgBatchSize = 1.74 (420.2 secs) (EloDiff baseline)
numSearchThreads =  4: 10 / 10 positions, visits/s = 26.62 nnEvals/s = 24.16 nnBatches/s = 8.23 avgBatchSize = 2.94 (301.7 secs) (EloDiff +110)
numSearchThreads =  5: 10 / 10 positions, visits/s = 22.60 nnEvals/s = 20.75 nnBatches/s = 5.57 avgBatchSize = 3.73 (355.8 secs) (EloDiff +39)
numSearchThreads =  6: 10 / 10 positions, visits/s = 24.76 nnEvals/s = 23.35 nnBatches/s = 4.91 avgBatchSize = 4.75 (325.1 secs) (EloDiff +61)
numSearchThreads =  8: 10 / 10 positions, visits/s = 24.67 nnEvals/s = 23.53 nnBatches/s = 3.45 avgBatchSize = 6.82 (327.0 secs) (EloDiff +38)
numSearchThreads = 12: 10 / 10 positions, visits/s = 19.97 nnEvals/s = 19.65 nnBatches/s = 1.84 avgBatchSize = 10.67 (406.1 secs) (EloDiff -86)


Based on some test data, each speed doubling gains perhaps ~250 Elo by searching deeper.
Based on some test data, each thread costs perhaps 7 Elo if using 800 visits, and 2 Elo if using 5000 visits (by making MCTS worse).
So APPROXIMATELY based on this benchmark, if you intend to do a 5 second search:
numSearchThreads =  3: (baseline)
numSearchThreads =  4:  +110 Elo (recommended)
numSearchThreads =  5:   +39 Elo
numSearchThreads =  6:   +61 Elo
numSearchThreads =  8:   +38 Elo
numSearchThreads = 12:   -86 Elo

If you care about performance, you may want to edit numSearchThreads in C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_gpu.cfg based on the above results!
If you intend to do much longer searches, configure the seconds per game move you expect with the '-time' flag and benchmark again.
If you intend to do short or fixed-visit searches, use lower numSearchThreads for better strength, high threads will weaken strength.
If interested see also other notes about performance and mem usage in the top of C:\Users\foxrain\Downloads\katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64\gtp_gpu.cfg

2026-08-05 20:45:37+0800: GPU -1 finishing, processed 45325 rows 12175 batches

@Looong01

Looong01 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@foxrainowo Thanks again for the detailed testing — your logs (plus a day of controlled experiments on my machine) let us root-cause the NPU crash, and the fixes are now in. Updated report:

Why gtp_npu.cfg crashed on NPU (not a code bug)

The ZE_RESULT_ERROR_DEVICE_LOST was not a bug in KataGo's code — it was a mismatch between runtime configuration and hardware characteristics:

  • b11/b40-class (v17) models contain real attention layers (MatMul/Transpose/Softmax-heavy). The shared ONNX builder (from upstream PR Add ONNX Runtime backend support (OpenVINO / DirectML / TensorRT / CPU, Windows & Linux) #1222) can only express those as data-dependent MatMul attention — it cannot fold them into static-weight convolutions the way older conv-only models (e.g. b28 "nbt" blocks, no attention) map. Intel's NPU compiler/driver is unstable on exactly this graph class.
  • Verified by isolation: the old v1.16.4 binary keeps working when we swap in the new ORT 1.28 + OpenVINO 2026.3 DLLs; the same graph runs fine on CPU and on the Intel GPU. Two failure modes on NPU: batch >= 6 -> compilation hangs forever (pure-OpenVINO repro, >60 min), and batch varying 1-5 -> intermittent DEVICE_LOST (4/4 runs, 11s to 10min). Batch pinned to 1 is always stable (your 34-min hybrid run; a 4200-inference pure-OpenVINO batch-1 loop; and a dedicated 32-min NPU-only batch-1 KataGo run, all clean).
  • NPU driver was already latest (32.0.100.4841). I will file this upstream to openvinotoolkit/openvino with the pure-OpenVINO repro.

Why GPU-only beat hybrid on your machine

Expected, two reasons: (1) your iGPU simply happens to be much stronger than the NPU for these models (~30x: 80 vs 2.7 visits/s in your own numbers), and (2) as above, v17 attention graphs can't be compiled into the NPU's preferred conv form, so the NPU contributes little. The NPU is a latency/efficiency-oriented accelerator (small MAC array, batch 1 already saturates it, batch N costs ~N latency for ~zero throughput gain) — that's why the hybrid config pins the NPU thread to batch 1 and lets the GPU thread do bulk batching. gtp_gpu.cfg >= gtp_npu_gpu_hybrid is the expected outcome here; hybrid/NPU only makes sense for smaller models or power-constrained scenarios.

Fixes shipped in the release configs (crash + perf regression)

  • gtp_npu.cfg: pinned nnMaxBatchSize = 1 (the stable regime; NPU loses nothing by it).
  • gtp_npu_gpu_hybrid.cfg: NPU thread stays pinned to batch 1; GPU thread unpinned (falls back to the global default).
  • Also: avoid benchmark autotune on NPU (it scales to 32 threads -> batch 32 -> hits the compile hang); use fixed -threads.

New backend work: making v17 NPU-friendly (+78% NPU throughput)

I extended the ONNX backend/builder with an NPU-conditional build path: when the session targets an NPU device, it can now emit a mask-free exact-board graph — the InputMask input path and all 33 attention mask-bias Add nodes ([1,heads,361,361] each, ~6-20% of NPU runtime, mathematically no-ops when the board exactly matches the network size) are dropped. Non-NPU targets build exactly as before.

Measured on b11 + NPU, batch 1: 2.73 -> 4.87 visits/s (+78%), 14 minutes / 3480 inferences with zero crashes, and fixed-seed numerical comparison vs the masked graph: 0.03c winrate / 0.4% policy delta (FP16 noise), same move choice and PV.

⚠️ Important caveat — read before using: without the mask, the net can no longer distinguish a padded board from a real one. For workloads where queries are smaller than the network size (e.g. analyzing 13x13 games on a 19x19 model), results would be silently wrong — not slower, wrong. So this is strictly opt-in: new config key onnxOpenVINONPUExactBoard (default false). It is enabled only in gtp_npu.cfg, where GTP play always uses the exact board size (the evaluator is recreated on any board-size change). Do NOT enable it for mixed-size analysis workloads.

Also fixed: OpenVINO compile cache never being created

You noticed the cache wasn't generated under gtp. Root cause: the backend left cache_dir unset by default, so every startup paid a full NPU recompile. The backend now caches compiled blobs to KataGo's standard data dir (<katago.exe dir>/KataGoData/openvino_cache on Windows, ~/.katago/openvino_cache on Linux; onnxOpenVINOCacheDir still overrides). First run writes the blob (~140 MB), later startups load it immediately.

Additionally, a new onnxOpenVINOLoadConfig key passes arbitrary OpenVINO device config (JSON) through to ONNX Runtime; the shipped configs use it to enable NPU compiler optimization-level=2 + performance-hint-override=latency (measured faster; NPU_TURBO deliberately left out — it was slower in testing).

It is here: https://github.com/Looong01/KataGo-Multi-backends/releases/tag/v1.17.1-openvino

@Looong01

Looong01 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@lightvector Could u pls check this? Update to v1.17.1 and NPU, GPU, Hybird mode fully tested. It's ready to merge.

@foxrainowo

foxrainowo commented Aug 9, 2026

Copy link
Copy Markdown

After updating the versions of onnxruntime and openvino to the latest versions, the NPU backend can run normally (without getting stuck), but the speed is extremely slow. In version 1.17.1, the performance of the GPU backend is excellent, with a speed of around 80v/s. I don't know what special optimizations are involved, but the result is really surprising. However, the performance of the NPU backend is very poor.

For comparison, b28_NPU_1.16.4 runs at 70v/s, while b40_NPU_1.16.4 runs at 30v/s. Assuming b11 >≈ b40, the speed of b11_NPU_1.17.1 should be greater than 30v/s.

Version b11_NPU b11_GPU b40_NPU b40_GPU
katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64-2 12 visits/s 80 visits/s (threads=5) 15 visits/s 20 visits/s
katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64 9 visits/s 80 visits/s (threads=10/12) 11 visits/s 25 visits/s
katago-v1.16.4-openvino2026.0-npu-windows-x64 Not supported Not supported 32 visits/s Not supported

@Looong01

Copy link
Copy Markdown
Contributor Author

After updating the versions of onnxruntime and openvino to the latest versions, the NPU backend can run normally (without getting stuck), but the speed is extremely slow. In version 1.17.1, the performance of the GPU backend is excellent, with a speed of around 80v/s. I don't know what special optimizations are involved, but the result is really surprising. However, the performance of the NPU backend is very poor.

For comparison, b28_NPU_1.16.4 runs at 70v/s, while b40_NPU_1.16.4 runs at 30v/s. Assuming b11 >≈ b40, the speed of b11_NPU_1.17.1 should be greater than 30v/s.

Version b11_NPU b11_GPU b40_NPU b40_GPU
katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64-2 12 visits/s 80 visits/s (threads=5) 15 visits/s 20 visits/s
katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64 9 visits/s 80 visits/s (threads=10/12) 11 visits/s 25 visits/s
katago-v1.16.4-openvino2026.0-npu-windows-x64 Not supported Not supported 32 visits/s Not supported

As I said, the new mode isn't suitable for NPU operation. As for the performance drop in the old model, I'll look into it and try to fix it next week (my Intel NPU machine has been borrowed). Thanks for your test report and effort!

@foxrainowo

foxrainowo commented Aug 19, 2026

Copy link
Copy Markdown

@Looong01
Update my test. I noticed that your project has copied some code from #1222. I knew there were some parameters that could be adjusted, so I tested your project's performance again using different parameters.

My previous test was conducted using the default configuration, whereas this time I adjusted onnxSkipScale8 and onnxTransformerNHWC. The results showed that their performance is quite different, which could also explain the reason for the previous result.

On the NPU, for b11c768h12nbt3, onnxTransformerNHWC=false provides a 2.6~3.5× speedup; for b40c768nbt, b28c512nbt, and b18c384nbt, onnxSkipScale8=true provides a 3× speedup.
On the GPU, for b11c768h12nbt3, onnxTransformerNHWC=true provides a 3× speedup; for b40c768nbt, b28c512nbt, and b18c384nbt, onnxSkipScale8=true provides a 2× speedup.

I noticed two things: your project is slower than #1222 and your project is able to run b40c768nbt, whereas that one cannot.

Would you prefer users to use your project or #1222? KataGo's developers merged with seniorfish's project, but it still has some bugs (The issue is here: #1238). So I'd prefer to use your project.

katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64-2 b11c768h12nbt3 b40c768nbt b28c512nbt b18c384nbt b60c320 b40c256
NPU_t1_onnxSkipScale8=false_onnxTransformerNHWC=false 26 11 23 46 68 126
NPU_t1_onnxSkipScale8=false_onnxTransformerNHWC=true 10 11 23 46 67 124
NPU_t1_onnxSkipScale8=true_onnxTransformerNHWC=false 26 32 71 145 73 140
NPU_t1_onnxSkipScale8=true_onnxTransformerNHWC=true 10 31 70 140 74 135
GPU_onnxSkipScale8=false_onnxTransformerNHWC=false 25 25 58 116 107 245
GPU_onnxSkipScale8=false_onnxTransformerNHWC=true 83 25 68 127 107 249
GPU_onnxSkipScale8=true_onnxTransformerNHWC=false 25 50 140 244 112 306
GPU_onnxSkipScale8=true_onnxTransformerNHWC=true 86 53 150 266 131 366

For reference:

katago-onnx-openvino-c025a30 b11c768h12nbt3 b40c768nbt b28c512nbt b18c384nbt b60c320 b40c256
NPU_t1_onnxSkipScale8=false_onnxTransformerNHWC=false 34 Failure 32 61 276
NPU_t1_onnxSkipScale8=false_onnxTransformerNHWC=true 10 Failure 32 59 266
NPU_t1_onnxSkipScale8=true_onnxTransformerNHWC=false 35 Failure 141 253 249
NPU_t1_onnxSkipScale8=true_onnxTransformerNHWC=true 10 Failure 135 244 267
GPU_onnxSkipScale8=false_onnxTransformerNHWC=false 26 30 90 182 334
GPU_onnxSkipScale8=false_onnxTransformerNHWC=true 105 33 88 189 320
GPU_onnxSkipScale8=true_onnxTransformerNHWC=false 29 56 169 427 330
GPU_onnxSkipScale8=true_onnxTransformerNHWC=true 100 56 165 431 321

…regression

Syncs the branch to upstream v1.17.2 and fixes a v1.16.4 -> v1.17.1 performance
regression on Intel NPU, which turned out to also be hiding a correctness bug.

1. scale8 outputs were decoded 8x too small (correctness).
   applyScale8ToReduceActivations() multiplies postProcessParams.outputScaleMultiplier
   by 8 to compensate for the 1/8-scaled graph, but maybeApplyScale8() ran in
   createComputeHandle, long after NNEvaluator had already snapshotted
   postProcessParams. The compensation was silently lost, so every convnet
   evaluation came back 8x too small (measured whiteLead 0.095 vs a true 0.756).
   This affected the default configuration. Snapshot postProcessParams after
   createComputeContext and apply scale8 there, matching upstream.

2. MISH_SCALE8 blocked Mish fusion (performance).
   The emission Softplus(8x) -> Tanh -> Mul(x, .) does not match the canonical
   Mish pattern Mul(u, Tanh(Softplus(u))), so OpenVINO could not fuse it and ran
   the chain unfused. Substituting u = 8x and using mish_scale8(x) = mish(8x)/8
   keeps an exact canonical Mish subgraph for two extra scalar multiplies, so the
   FP16 headroom scale8 provides is kept at full speed.

3. transformerNHWC now defaults per device.
   NHWC is faster on the OpenVINO GPU plugin but much slower on the NPU plugin, so
   default to NCHW whenever any OpenVINO server thread may target an NPU. A bare
   AUTO device string counts as possibly-NPU, since OpenVINO picks at runtime.

4. onnxOpenVINODeviceType now auto-selects when unset.
   Probe NPU, then GPU, then CPU via AppendExecutionProvider_OpenVINO_V2, which
   validates device_type immediately, and use the first available one. A machine
   with only an iGPU now works with no configuration. ONNX Runtime's own device
   enumeration is deliberately not used: on a provider-bridge OpenVINO build it
   reports CPU only even on machines that do have an NPU and an iGPU.

Measured on Intel NPU, single server thread (nnEvals/s), before -> after:
  kata1-b28c512nbt       10.56 -> 39.27   (v1.16.4 reference: 26.84)
  kata1-zhizi-b40c768nbt  5.23 -> 16.08
  b11c768h12nbt3tflrs     3.69 -> 10.12
  b10c384h6nbttflrs      12.93 -> 32.79
  b10c512h8nbt3tflrs      6.58 -> 16.48
OpenVINO GPU (8 threads) b11c768h12nbt3tflrs: 14.25 -> 16.94.

Verified that scale8-applied and scale8-skipped now produce identical outputs in
CPU fp32, as they must since the transform is mathematically exact. Verified the
device fallback by removing openvino_intel_npu_plugin.dll to simulate a machine
without an NPU: it auto-selects GPU and switches to NHWC.

Also documents all device/layout selection cases in README.md and the example
configs.
@Looong01

Copy link
Copy Markdown
Contributor Author

@Looong01 Update my test. I noticed that your project has copied some code from #1222. I knew there were some parameters that could be adjusted, so I tested your project's performance again using different parameters.

My previous test was conducted using the default configuration, whereas this time I adjusted onnxSkipScale8 and onnxTransformerNHWC. The results showed that their performance is quite different, which could also explain the reason for the previous result.

On the NPU, for b11c768h12nbt3, onnxTransformerNHWC=false provides a 2.6~3.5× speedup; for b40c768nbt, b28c512nbt, and b18c384nbt, onnxSkipScale8=true provides a 3× speedup. On the GPU, for b11c768h12nbt3, onnxTransformerNHWC=true provides a 3× speedup; for b40c768nbt, b28c512nbt, and b18c384nbt, onnxSkipScale8=true provides a 2× speedup.

I noticed two things: your project is slower than #1222 and your project is able to run b40c768nbt, whereas that one cannot.

Would you prefer users to use your project or #1222? KataGo's developers merged with seniorfish's project, but it still has some bugs (The issue is here: #1238). So I'd prefer to use your project.

katago-v1.17.1-onnxruntime1.28.0-openvino2026.3-windows-x64-2 b11c768h12nbt3 b40c768nbt b28c512nbt b18c384nbt b60c320 b40c256
NPU_t1_onnxSkipScale8=false_onnxTransformerNHWC=false 26 11 23 46 68 126
NPU_t1_onnxSkipScale8=false_onnxTransformerNHWC=true 10 11 23 46 67 124
NPU_t1_onnxSkipScale8=true_onnxTransformerNHWC=false 26 32 71 145 73 140
NPU_t1_onnxSkipScale8=true_onnxTransformerNHWC=true 10 31 70 140 74 135
GPU_onnxSkipScale8=false_onnxTransformerNHWC=false 25 25 58 116 107 245
GPU_onnxSkipScale8=false_onnxTransformerNHWC=true 83 25 68 127 107 249
GPU_onnxSkipScale8=true_onnxTransformerNHWC=false 25 50 140 244 112 306
GPU_onnxSkipScale8=true_onnxTransformerNHWC=true 86 53 150 266 131 366
For reference:

katago-onnx-openvino-c025a30 b11c768h12nbt3 b40c768nbt b28c512nbt b18c384nbt b60c320 b40c256
NPU_t1_onnxSkipScale8=false_onnxTransformerNHWC=false 34 Failure 32 61 276
NPU_t1_onnxSkipScale8=false_onnxTransformerNHWC=true 10 Failure 32 59 266
NPU_t1_onnxSkipScale8=true_onnxTransformerNHWC=false 35 Failure 141 253 249
NPU_t1_onnxSkipScale8=true_onnxTransformerNHWC=true 10 Failure 135 244 267
GPU_onnxSkipScale8=false_onnxTransformerNHWC=false 26 30 90 182 334
GPU_onnxSkipScale8=false_onnxTransformerNHWC=true 105 33 88 189 320
GPU_onnxSkipScale8=true_onnxTransformerNHWC=false 29 56 169 427 330
GPU_onnxSkipScale8=true_onnxTransformerNHWC=true 100 56 165 431 321

Fixed.

@Looong01

Copy link
Copy Markdown
Contributor Author

Update: synced to v1.17.2, and fixed the NPU performance regression reported above

Thanks @foxrainowo for the detailed benchmark tables — they pointed straight at the
cause. Investigating it also turned up a correctness bug that affected the default
configuration, so this is worth a look beyond just the speed numbers.

1. scale8 was decoding outputs 8x too small (correctness bug)

applyScale8ToReduceActivations() scales the graph to 1/8 and compensates by multiplying
postProcessParams.outputScaleMultiplier by 8. But maybeApplyScale8() ran in
createComputeHandle, while NNEvaluator snapshots postProcessParams right after
createComputeContext returns — so the compensation was silently dropped.

Symptom: whiteLead came back as 0.095 where the true value is 0.756, i.e. exactly 1/8.
Since onnxSkipScale8 defaults to false, every convnet evaluation was affected.

This is the same bug upstream fixed in the merged ONNX backend; I've ported that fix
(snapshot moved after createComputeContext, scale8 applied there).

2. MISH_SCALE8 was blocking OpenVINO's Mish fusion (the actual slowdown)

The emitted subgraph was Softplus(8x) -> Tanh -> Mul(x, ·). OpenVINO fuses Mish by matching
Mul(u, Tanh(Softplus(u))), but here the final Mul takes x, not 8x, so the pattern
didn't match and the whole chain ran unfused.

Rather than disabling scale8 (which would give up its FP16 headroom), I used the identity
mish_scale8(x) = mish(8x)/8, which keeps an exact canonical Mish subgraph and costs only two
extra scalar multiplies. So scale8 stays on by default and is still fast — no config change
needed, and onnxSkipScale8=true is no longer necessary as a workaround.

3. onnxTransformerNHWC now defaults per device

This explains the other half of @foxrainowo's table: the two OpenVINO plugins want opposite
layouts, so no single default can be right. It's now NCHW when any OpenVINO thread may target an
NPU, NHWC otherwise. A bare AUTO counts as possibly-NPU, since OpenVINO picks at runtime and
guessing NCHW costs a GPU ~1.3x but saves an NPU ~2.7x.

4. Device is auto-selected when onnxOpenVINODeviceType is unset

Probes NPU → GPU → CPU and uses the first available, so a machine with only an iGPU works with no
configuration. The probe uses AppendExecutionProvider_OpenVINO_V2, which validates device_type
immediately and needs no model or session.

Worth noting for anyone attempting something similar: ONNX Runtime's own device enumeration
(Env::GetEpDevices()) is not usable here — on a provider-bridge OpenVINO build it reports
CPU only, even on a machine that does have an NPU and an iGPU. I tried it first and it would have
silently downgraded exactly the setups this branch targets.

Results (Intel NPU, 1 server thread, nnEvals/s)

Model before after v1.16.4
kata1-b28c512nbt 10.56 39.27 26.84
kata1-zhizi-b40c768nbt 5.23 16.08
b11c768h12nbt3tflrs 3.69 10.12
b10c384h6nbttflrs 12.93 32.79
b10c512h8nbt3tflrs 6.58 16.48

OpenVINO GPU (b11c768h12nbt3tflrs, 8 threads): 14.25 → 16.94.

All of these are now at or above v1.16.4, with the defaults, without tuning onnxSkipScale8 or
onnxTransformerNHWC by hand.

Verification

  • scale8-applied and scale8-skipped now produce identical outputs in CPU fp32, as they must
    since the transform is mathematically exact (before: 0.522 vs 0.669 winrate).
  • Device fallback verified by removing openvino_intel_npu_plugin.dll to simulate a machine with
    no NPU: it auto-selects GPU and switches to NHWC.
  • README.md and the example configs now document every device/layout selection case.

@foxrainowo

Copy link
Copy Markdown

@Looong01

Thanks for these fixes.

I previously reported the crash issue at #1238, and I asked AI about the reason. It said that the net's batch is dynamic, but the NPU needs it to remain constant during compilation.

KataGo just released the new version 1.18.0. It added a new onnxPadBatch function, which says: "Pad every neural net evaluation up to the max batch size. Some providers (DirectML, and OpenVINO on an NPU) recompile whenever the batch size changes, which makes search extremely slow without padding. Auto (the default) enables it exactly for those." I have tried this new version, and it runs normally, but the speed is very slow.

I don't know why your project does not encounter this crash issue and is able to run normally. What I mean is that your project is fast, accurate, and compatible.

Could you take a look and see if there are any other areas that could be improved, and then compile the final version for me to test?

Looong01 and others added 4 commits August 23, 2026 16:39
Upstream merged its own ONNX backend (PR lightvector#1222) and released v1.18.0 after this
branch forked, so every ONNX file collided. Resolved by taking upstream wholesale
- including cpp/neuralnet/onnxbackend.cpp, which was an add/add conflict between
two independent implementations - and re-applying only the Intel NPU fixes
upstream does not have. Everything not specific to the Intel NPU is byte-identical
to upstream; the diff is four files.

Upstream already carries the scale8 postProcessParams ordering fix, so that one is
not re-applied here.

1. MISH_SCALE8 blocks OpenVINO's Mish fusion.
   Upstream emits Mul(x, Tanh(Softplus(Mul(x,8)))). Fusion passes match the
   canonical Mul(u, Tanh(Softplus(u))), and here the outer Mul takes x while
   Softplus takes 8x, so the pattern does not match and OpenVINO runs Softplus,
   Tanh and Mul unfused over the whole trunk. Emit mish_scale8(x) = mish(8x)/8
   instead, with u = 8x, which contains an exact canonical Mish subgraph and costs
   two extra scalar multiplies. Same function, numerically identical.

   kata1-b28c512nbt on an Intel NPU at 8 search threads, same avgBatchSize:
   5.85 -> 28.17 nnEvals/s.

2. onnxTransformerNHWC defaults per device.
   The two OpenVINO plugins prefer opposite trunk layouts, so upstream's fixed
   default of true cannot be right for both. On b11c768h12nbt3tflrs at 8 search
   threads the NPU runs NCHW about 1.75x faster than NHWC, while the GPU runs NHWC
   about 1.5x faster than NCHW. Default to NCHW as soon as any OpenVINO server
   thread might target an NPU. A bare AUTO counts as might-be-NPU: OpenVINO
   resolves it at runtime and does not report the choice back.

3. onnxOpenVINODeviceType auto-selects when unset.
   Probe NPU then GPU via AppendExecutionProvider_OpenVINO_V2, which validates
   device_type immediately and needs no model or session, and use the first one
   present. CPU is not a candidate since upstream rejects CPU-only OpenVINO device
   strings. An explicit setting is always honored as-is, including when the device
   does not exist. ONNX Runtime's own enumeration (Ort::Env::GetEpDevices) is
   deliberately not used: on a provider-bridge OpenVINO build it reports CPU only,
   even on a machine that has both an NPU and an iGPU.

Also updates the onnxOpenVINODeviceType, onnxTransformerNHWC and onnxSkipScale8
documentation in gtp_example.cfg and the OpenVINO section of Compiling.md.

Dropped in favor of upstream's equivalents: the exportonnx command (upstream has
dumponnx), the .onnx suffixes in loadmodel.cpp (upstream handles .onnx in
desc.cpp), the ONNX prompts in genconfig, runonnxtests.sh (upstream has
runonnxmodelfiletests), and the per-thread nnMaxBatchSize plumbing in the test
helpers. The fork-only OpenVINO keys (onnxOpenVINOLoadConfig,
onnxOpenVINONPUExactBoard, onnxOpenVINODeviceId, onnxOpenVINOEnableNPUFastCompile,
onnxOpenVINOModelPriority, onnxOpenVINONumOfThreads, onnxModelVersion,
onnxInput*/onnxOutput*) are gone too: upstream deliberately slimmed the OpenVINO
option set, and its .onnx file support supersedes the raw-loading keys. Measured
on an Intel NPU, onnxOpenVINOLoadConfig made no difference and
onnxOpenVINONPUExactBoard was worth about 1.2x.

Verified on an Intel NPU machine: auto-select logs "auto-selected 'NPU'"; a
transformer model on it builds with transformerNHWC=false while an explicit GPU
device builds with transformerNHWC=true; and dumponnx output contains the fusable
mish8/inv8 subgraph.
@Looong01

Copy link
Copy Markdown
Contributor Author

Rebased onto v1.18.0 — three Intel NPU fixes remain

Now that the ONNX backend is merged upstream, I've taken upstream's version of
everything and re-applied only what is specific to the Intel NPU. Every file not
specific to the Intel NPU is byte-identical to master. The fork-only OpenVINO
config keys this branch used to carry are gone, since the slim down OpenVINO config options refactor deliberately removed that surface and the .onnx file
support supersedes the raw-loading keys. The scale8 postProcessParams ordering
fix is already in master, so it is not here.

Measurements below are from an Intel Core Ultra NPU and its iGPU, OpenVINO 2026.3
/ ONNX Runtime 1.28, at 8 search threads. Benchmark arms were run alternating and
repeated, with avgBatchSize checked to match across arms before comparing —
run-to-run spread on this machine is over 10%, and non-interleaved single runs
gave me the opposite of the truth more than once.

1. MISH_SCALE8 prevents Mish fusion, and goes non-finite on the OpenVINO GPU

onnxmodelbuilder.cpp currently emits Mul(x, Tanh(Softplus(Mul(x, 8)))). Mish
fusion passes match the canonical shape Mul(u, Tanh(Softplus(u))), and here the
outer Mul takes x while Softplus takes 8x, so the operands differ, the
pattern does not match, and OpenVINO runs Softplus, Tanh and Mul as three
separate ops across the entire trunk.

Emitting the same function through its own definition,
mish_scale8(x) = mish(8x)/8 with u = 8x, gives
Mul(Mul(u, Tanh(Softplus(u))), 1/8) — an exact canonical Mish subgraph plus two
scalar multiplies. Numerically identical:
8x·tanh(softplus(8x))/8 == x·tanh(softplus(8x)).

On the NPU this is a 4.8x throughput difference. kata1-b28c512nbt, two
binaries differing only in this file, three alternating runs each:

nnEvals/s
master's emission 5.85 / 5.81 / 5.75
this PR 28.17 / 27.96 / 27.49

On the GPU it is a correctness bug rather than a slowdown. With the default
onnxSkipScale8 = false, master's emission produces non-finite outputs and
KataGo dies with Got nonfinite for policy sum; GPU utilization then drops to
zero and the process hangs instead of exiting.

emission onnxSkipScale8 GPU result
master false (default) non-finite, hang (2/2)
master true 25.60 nnEvals/s
this PR false (default) 24.24 nnEvals/s
this PR true 25.63 nnEvals/s

Turning scale8 off puts both binaries on the same plain-Mish path and both work,
so the failure needs scale8 applied and the non-canonical emission. My guess is
that the unfused log(1+exp(8x)) overflows the FP16 the plugin infers in, while
the fused Mish evaluates it stably — but the isolation above holds regardless of
the mechanism.

The last two rows also show scale8 costs only ~5% on the GPU once the emission is
fusable, so the gtp_example.cfg comment describing it as a ~2x/~4x tradeoff no
longer applies; I've updated that text.

Worth flagging separately from this PR: a non-finite NN output kills the async bot
thread but leaves the process hanging rather than exiting with an error.

2. onnxTransformerNHWC cannot have one right default

The two OpenVINO plugins prefer opposite trunk layouts, so the fixed default of
true is wrong on one of them either way. b11c768h12nbt3tflrs, two runs each:

device NCHW NHWC
NPU 5.44 / 5.49 3.17 / 3.12
GPU 11.61 / 11.57 16.21 / 18.33

So the NPU prefers NCHW by ~1.75x and the GPU prefers NHWC by ~1.5x. This PR
picks per device when the key is unset, defaulting to NCHW as soon as any
OpenVINO server thread might target an NPU. A bare AUTO counts as might-be-NPU:
OpenVINO resolves it at runtime and does not report the choice back through ORT's
API, and guessing NCHW costs a GPU ~1.5x when wrong but saves an NPU ~1.75x when
right.

3. onnxOpenVINODeviceType auto-selects when unset

Probes NPU, then GPU, and uses the first one present, so a machine with only an
iGPU and a machine with an NPU both work with no device configuration. The probe
calls AppendExecutionProvider_OpenVINO_V2, which validates device_type
immediately and throws [OpenVINO] Device X is not available, so it needs no
model and no session. CPU is not a candidate, since the provider already rejects
CPU-only device strings. An explicit setting is always honored as-is, including
when the device does not exist — failing with OpenVINO's own error beats quietly
running somewhere else.

Worth recording for anyone attempting the same: ONNX Runtime's own device
enumeration (Ort::Env::GetEpDevices, OrtHardwareDeviceType) is not usable
here. On a provider-bridge OpenVINO build it reports the CPU only, even on a
machine that does have both an NPU and an iGPU. I implemented it that way first
and it would have silently steered exactly the setups this is meant to help onto
the slowest device present.

Not part of the upstream-facing change

The branch also carries -DUSE_ONNX_EP=OPENVINO, which only defaults
ONNXRUNTIME_ROOT to an OpenVINO-enabled ONNX Runtime unpacked under
cpp/external. It is a local build convenience for this fork and changes nothing
at runtime; happy to drop it from the PR if you'd rather not carry it.

@Looong01

Copy link
Copy Markdown
Contributor Author

@Looong01

Thanks for these fixes.

I previously reported the crash issue at #1238, and I asked AI about the reason. It said that the net's batch is dynamic, but the NPU needs it to remain constant during compilation.

KataGo just released the new version 1.18.0. It added a new onnxPadBatch function, which says: "Pad every neural net evaluation up to the max batch size. Some providers (DirectML, and OpenVINO on an NPU) recompile whenever the batch size changes, which makes search extremely slow without padding. Auto (the default) enables it exactly for those." I have tried this new version, and it runs normally, but the speed is very slow.

I don't know why your project does not encounter this crash issue and is able to run normally. What I mean is that your project is fast, accurate, and compatible.

Could you take a look and see if there are any other areas that could be improved, and then compile the final version for me to test?

Binary release here: https://github.com/Looong01/KataGo-Multi-backends/releases/tag/v1.18.0-openvino

@Looong01

Copy link
Copy Markdown
Contributor Author

Rebased onto v1.18.0 — three Intel NPU fixes remain

Now that the ONNX backend is merged upstream, I've taken upstream's version of
everything and re-applied only what is specific to the Intel NPU. Every file not
specific to the Intel NPU is byte-identical to master. The fork-only OpenVINO
config keys this branch used to carry are gone, since the slim down OpenVINO config options refactor deliberately removed that surface and the .onnx file
support supersedes the raw-loading keys. The scale8 postProcessParams ordering
fix is already in master, so it is not here.

How the numbers below were taken

Intel Core Ultra NPU and its iGPU, OpenVINO 2026.3 / ONNX Runtime 1.28. Arms were
run alternating within each model rather than one arm then the other, repeated
at least twice, and avgBatchSize was checked to match across arms before any
comparison. Run-to-run spread on this machine exceeds 10% and throws outliers, and
non-interleaved single runs gave me the opposite of the truth more than once
while preparing this — so please discount any earlier table I posted that lacks
these conditions.


1. MISH_SCALE8 prevents Mish fusion, and goes non-finite on the OpenVINO GPU

onnxmodelbuilder.cpp currently emits Mul(x, Tanh(Softplus(Mul(x, 8)))). Mish
fusion passes match the canonical shape Mul(u, Tanh(Softplus(u))), and here the
outer Mul takes x while Softplus takes 8x, so the operands differ, the
pattern does not match, and OpenVINO runs Softplus, Tanh and Mul as three separate
ops across the entire trunk.

Emitting the same function through its own definition,
mish_scale8(x) = mish(8x)/8 with u = 8x, gives
Mul(Mul(u, Tanh(Softplus(u))), 1/8) — an exact canonical Mish subgraph plus two
scalar multiplies. Numerically identical:
8x·tanh(softplus(8x))/8 == x·tanh(softplus(8x)).

On the GPU this is a correctness bug rather than a slowdown. With the default
onnxSkipScale8 = false, master's emission produces non-finite outputs and KataGo
dies with Got nonfinite for policy sum; GPU utilization then drops to zero and
the process hangs instead of exiting. kata1-b28c512nbt, OpenVINO GPU:

emission onnxSkipScale8 result
master false (default) non-finite, hang (2/2)
master true 25.60 nnEvals/s
this PR false (default) 24.24 nnEvals/s
this PR true 25.63 nnEvals/s

Turning scale8 off puts both binaries on the same plain-Mish path and both work,
so the failure needs scale8 applied and the non-canonical emission. My guess is
that the unfused log(1+exp(8x)) overflows the FP16 the plugin infers in, while
the fused Mish evaluates it stably — but the isolation above holds regardless of
the mechanism.

The last two rows also show scale8 costs only ~5% on the GPU once the emission is
fusable, so the gtp_example.cfg comment describing it as a ~2x/~4x tradeoff no
longer applies; I've updated that text. onnxSkipScale8 = true is no longer
needed as a workaround for anything.

Worth flagging separately from this PR: a non-finite NN output kills the async bot
thread but leaves the process hanging rather than exiting with an error.

2. onnxTransformerNHWC cannot have one right default

The two OpenVINO plugins prefer opposite trunk layouts, so the fixed default of
true is wrong on one of them either way. b11c768h12nbt3tflrs, two runs each:

device NCHW NHWC
NPU 5.44 / 5.49 3.17 / 3.12
GPU 11.61 / 11.57 16.21 / 18.33

The NPU prefers NCHW by ~1.75x and the GPU prefers NHWC by ~1.5x. This PR picks
per device when the key is unset, defaulting to NCHW as soon as any OpenVINO
server thread might target an NPU. A bare AUTO counts as might-be-NPU: OpenVINO
resolves it at runtime and does not report the choice back through ORT's API, and
guessing NCHW costs a GPU ~1.5x when wrong but saves an NPU ~1.75x when right.

3. onnxOpenVINODeviceType auto-selects when unset

Probes NPU, then GPU, and uses the first one present, so a machine with only an
iGPU and a machine with an NPU both work with no device configuration. The probe
calls AppendExecutionProvider_OpenVINO_V2, which validates device_type
immediately and throws [OpenVINO] Device X is not available, so it needs no
model and no session. CPU is not a candidate, since the provider already rejects
CPU-only device strings. An explicit setting is always honored as-is, including
when the device does not exist — failing with OpenVINO's own error beats quietly
running somewhere else.

Worth recording for anyone attempting the same: ONNX Runtime's own device
enumeration (Ort::Env::GetEpDevices, OrtHardwareDeviceType) is not usable
here. On a provider-bridge OpenVINO build it reports the CPU only, even on a
machine that does have both an NPU and an iGPU. I implemented it that way first
and it would have silently steered exactly the setups this is meant to help onto
the slowest device present.


Results: five models on the Intel NPU

Two binaries differing only in onnxbackend.cpp and onnxmodelbuilder.cpp, same
runtime, -t 4, -v 80, alternating, two runs each. nnEvals/s;
avgBatchSize was 1.94–1.95 in all twenty runs, and the two runs of each arm
differ by under 1%.

model kind scale8 master this PR
kata1-b28c512nbt convnet applied 5.99 / 5.98 27.60 / 28.45 4.68x
kata1-zhizi-b40c768nbt convnet applied 2.70 / 2.68 10.50 / 10.47 3.90x
b11c768h12nbt3tflrs transformer n/a 3.41 / 3.43 5.90 / 5.90 1.73x
b10c384h6nbttflrs transformer n/a 11.36 / 11.45 20.39 / 20.37 1.79x
b10c512h8nbt3tflrs transformer n/a 5.70 / 5.71 10.07 / 10.14 1.77x

The split falls out exactly where the two fixes apply, which is a useful check on
the whole story: the convnets take the scale8 path and are carried by fix 1, at
3.9–4.7x. The transformers never get scale8 (their internal RMSNorm would undo the
scaling), so fix 1 cannot touch them and they are carried by fix 2 alone — landing
at 1.73x / 1.79x / 1.77x, which independently reproduces the ~1.75x measured
directly for the layout in section 2.

Thread scaling

The two are not fastest at the same thread count, so single-thread-count
comparisons are misleading. kata1-b28c512nbt:

threads this PR
1 14.49 / 14.75
4 27.60 / 28.45
8 27.49 – 28.17

b11c768h12nbt3tflrs on this PR peaks at the same place: 3.03 (t=1), 3.06 (t=2),
5.78 (t=4), 5.61 (t=8), 5.14 (t=16). At t=32 the NPU hung during graph
compilation and had to be killed.


What this PR does not fix

I also compared against the older v1.16.4 ONNX build I had packaged, on
kata1-b28c512nbt. It is still faster than master-plus-this-PR:

build t=1 t=2 t=4 t=8
v1.16.4 (ORT 1.24.4 / OV 2026.1) 30.75 / 30.93 30.62 22.11 9.25
v1.18.0 + this PR (ORT 1.28 / OV 2026.3) 14.49 / 14.75 27.60 / 28.45 27.49–28.17
v1.18.0 master 3.10 / 3.09 5.99 / 5.98

Peak to peak that is 30.93 vs 28.45, so this PR gets to about 92% of the old
build, and at t=1 it is only about half. I have not investigated where the rest
goes, and I would not assume it is KataGo's code: two ONNX Runtime versions, two
OpenVINO versions and the whole backend rewrite sit between those two rows. I
mention it so the numbers above are not read as "back to the old speed" — they are
not. What they show is master compared against master with three fixes, on one
machine, with everything else held constant.


Not part of the upstream-facing change

The branch also carries -DUSE_ONNX_EP=OPENVINO, which only defaults
ONNXRUNTIME_ROOT to an OpenVINO-enabled ONNX Runtime unpacked under
cpp/external. It is a local build convenience for this fork and changes nothing
at runtime; happy to drop it from the PR if you'd rather not carry it.

@Looong01

Copy link
Copy Markdown
Contributor Author

On the older v1.16.4 ONNX build

I had an older v1.16.4 ONNX/OpenVINO package around and checked this branch
against it, since a slower-than-before report is what started this. There is no
regression — at its best configuration this branch is about 40% faster than that
build, 43.5 vs 30.9 nnEvals/s on kata1-b28c512nbt (22.6 ms vs 33 ms per
inference call).

What made it look otherwise is onnxPadBatch, whose auto rule turns padding on for
any OpenVINO NPU. That is right at realistic thread counts and expensive below
them:

search threads onnxPadBatch on off
1 14.81 43.49
2 14.67 30.69
4 27.32 17.10
8 27.89 7.96

The crossover is between 2 and 4 threads. Compare at one fixed low thread count and
this branch looks half as fast as v1.16.4; compare at each build's own optimum and
it is well ahead.

I am not proposing a change to padsBatchForDevice in this PR. The rule is
genuinely thread-count dependent rather than simply wrong, t >= 4 is the normal
case, and I could not explain the mechanism: it is not the wasted arithmetic of
padding, because forcing nnMaxBatchSize = 1 at t = 1, which makes padding a
no-op, still gives 14.15. Something in the FixedShape path itself costs the 3x.
Pinning that down needs ORT profiling I have not done. Reporting the matrix in case
it is useful to whoever owns that design.

For completeness, these were each ruled out by a controlled experiment before I got
to onnxPadBatch: the ONNX Runtime 1.24.4 → 1.28 and OpenVINO 2026.1 → 2026.3
upgrades (same executable, swapped runtime DLLs: 29.45 / 29.70 → 29.60 / 29.75, so
neither runtime regressed); scale8's extra multiplies (~5% on both NPU and GPU);
nodes falling back to CPU (no EP fallback warnings, the whole graph is on
OpenVINO); and graph structure — the current emitter is if anything simpler than
the old one, using ReduceMean where the old graph hand-rolled
ReduceSum/Sub/Sqrt/Div, and Conv where it used MatMul + Reshape.

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.

6 participants