Skip to content

Propagate global --cache_dir to continuous batching pipeline (#4230)#4329

Open
exzile wants to merge 4 commits into
openvinotoolkit:mainfrom
exzile:fix/cache-dir-continuous-batching
Open

Propagate global --cache_dir to continuous batching pipeline (#4230)#4329
exzile wants to merge 4 commits into
openvinotoolkit:mainfrom
exzile:fix/cache-dir-continuous-batching

Conversation

@exzile

@exzile exzile commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #4230.

The continuous batching servable initializer constructs the GenAI ContinuousBatchingPipeline directly and never applied the server-level --cache_dir (ServerSettings.cacheDir). Unlike the non-CB path — which applies it via ModelInstance::setCacheOptions (ieCore.set_property(ov::cache_dir(...))) — the CB path left model-compilation caching disabled unless the user manually duplicated the value into the node's plugin_config as CACHE_DIR. As a result, .blob/.cl_cache artifacts were never persisted and every server restart fully recompiled the model.

Fix

In ContinuousBatchingServableInitializer::initialize, inject the global cache_dir into the pipeline pluginConfig (keyed by ov::cache_dir.name() == CACHE_DIR) right after parsing the node plugin_config and before constructing the pipeline. An explicit CACHE_DIR in the node's plugin_config remains authoritative.

const std::string& globalCacheDir = Config::instance().cacheDir();
if (!globalCacheDir.empty()) {
    if (properties->pluginConfig.find(ov::cache_dir.name()) == properties->pluginConfig.end()) {
        properties->pluginConfig[ov::cache_dir.name()] = globalCacheDir;
    } // else: explicit node CACHE_DIR wins
}

This also applies to the VLM continuous-batching servable, which reuses this initializer.

Testing

Added a regression test LLMNodeOptionsCacheDirPropagation (run for both the CB and VLM fixtures) covering:

  1. The global --cache_dir is propagated into pluginConfig["CACHE_DIR"] when the node does not set it.
  2. An explicit CACHE_DIR in the node plugin_config takes precedence over the global value.

Built and ran locally on Windows (MSVC) against a real facebook/opt-125m continuous-batching pipeline:

[       OK ] LLMOptionsHttpTest.LLMNodeOptionsCheckPluginConfig
[       OK ] LLMOptionsHttpTest.LLMNodeOptionsCacheDirPropagation
[  PASSED  ] 2 tests.

🤖 Generated with Claude Code

@exzile

exzile commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

End-to-end verification on GPU

Beyond the unit test, I verified the actual caching behavior on an Intel Arc Pro B70 dGPU (EXPORT_IMPORT capable) serving facebook/opt-125m via continuous batching with device: "GPU" and a --cache_dir pointing at an initially-empty directory:

Run 1 (empty cache):

  • Log confirms the fix path: servable_initializer.cpp] Applying global cache_dir to continuous batching pipeline: .../cachedir
  • Cache dir went from empty → *.cl_cache files + a 130 MB .blob (compiled model)
  • Pipeline init → AVAILABLE: ~1.6 s

Run 2 (restart, populated cache):

  • Same fix path fires
  • No recompile: blob is reused (file unchanged), no new artifacts written
  • Pipeline init → AVAILABLE: ~0.24 s (~6.7× faster cold start)

Without the change the cache directory stays empty on the CB path and every restart recompiles. With it, blobs persist and are reused across restarts as intended.

The continuous batching servable initializer constructs the GenAI
ContinuousBatchingPipeline directly and never applied the server-level
--cache_dir (ServerSettings.cacheDir). Unlike the non-CB path, which
applies it via ModelInstance::setCacheOptions, the CB path left model
compilation caching disabled unless the user duplicated the value into
the node's plugin_config as CACHE_DIR. As a result, .blob/.cl_cache
artifacts were never persisted and every restart fully recompiled the
model.

Inject the global cache_dir into the pipeline plugin config before
constructing the pipeline. An explicit CACHE_DIR in the node's
plugin_config remains authoritative.

Adds a regression test (LLMNodeOptionsCacheDirPropagation) covering both
propagation of the global value and precedence of an explicit node value.

Fixes openvinotoolkit#4230

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@exzile exzile force-pushed the fix/cache-dir-continuous-batching branch from 6fd48db to c45573c Compare June 27, 2026 01:04
LLMNodeOptionsCacheDirPropagation only asserts that --cache_dir lands in
properties->pluginConfig, which doesn't prove OpenVINO Core actually persists
compiled-model cache artifacts -- the actual symptom in openvinotoolkit#4230 (log said
"cache enabled", nothing was ever written to disk). Addresses feedback on
the issue: openvinotoolkit#4230 (comment)

LLMNodeOptionsCacheDirWritesCacheArtifacts constructs a real
ContinuousBatchingPipeline against a temp --cache_dir and asserts at least
one cache file actually lands there. Verified locally on Windows (MSVC)
against facebook/opt-125m:

[       OK ] LLMOptionsHttpTest.LLMNodeOptionsCacheDirWritesCacheArtifacts (707 ms)
@dtrawins dtrawins requested review from dkalinowski and mzegla July 9, 2026 09:44
Comment thread src/test/llm/llmnode_test.cpp Outdated

// Restore the global cache_dir so the singleton does not leak into other tests.
char* reset_argv[] = {(char*)"ovms", (char*)"--model_path", (char*)"/path/to/model", (char*)"--model_name", (char*)"some_name", (char*)"--rest_port", (char*)"8080"};
ovms::Config::instance().parse(7, reset_argv);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably need this restore also after any ASSERT in this suite. check how other tests do that via guard patter or SetUp/TearDown mechanic (some tests recover env variables this way)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dkalinowski Added a GlobalCacheDirGuard RAII struct that restores the Config singleton (and removes the temp cache dir) on scope exit, so a failed ASSERT_* mid-test can no longer leak the modified --cache_dir into later tests. Used it in both cache tests; removed the manual end-of-test restores.
Let me know if you want to change this or go a different route.

// this initializer constructs the pipeline directly, so the server-level cache_dir
// is otherwise never applied. An explicit CACHE_DIR in the node's plugin_config
// remains authoritative.
const std::string& globalCacheDir = Config::instance().cacheDir();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes it for language_model + continuous_batching

what about:

  • visual_language_model + legacy
  • visual_language_model + continuous_batching
  • language_model + legacy
    ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Investigated all four paths. VLM_CB already shares the same ContinuousBatchingServableInitializer, so it was already covered.
**The two legacy paths (LM and VLM) construct GenAI pipelines directly and had the same bug — global --cache_dir was never applied.

Extracted the propagation into a shared GenAiServableInitializer::applyGlobalCacheDir() helper (matching the existing loadChatTemplate static pattern), now called by all three initializers (base, CB, LM-legacy, VLM-legacy). This eliminates the duplication and covers every path through one tested code path.

This should suffice, if not we can rewire it a different way. @dkalinowski

exzile and others added 2 commits July 9, 2026 09:27
Extract the global --cache_dir propagation into a shared
GenAiServableInitializer::applyGlobalCacheDir helper and call it from all
GenAI initializers. The continuous batching path already applied it (and
VLM_CB shares that same initializer), but the legacy LM and legacy VLM
paths construct their pipelines directly and never applied the
server-level cache_dir. Routing every path through one helper covers all
four pipeline types (LM/VLM x CB/legacy) and removes the duplicated
inline block from the CB initializer.

In the cache_dir tests, replace the manual end-of-test Config restore
with a GlobalCacheDirGuard RAII helper so a failed ASSERT mid-test can no
longer leak the modified --cache_dir singleton into subsequent tests in
the suite. The guard also removes the temporary cache directory on scope
exit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

--cache_dir not propagated to LLM continuous batching pipeline (regression vs 2025.4 promise)

2 participants