Add partial cache generation when only one of the vendros is changed to speed up recalculation time

This commit is contained in:
ExPikaPaka
2026-06-15 09:28:59 +02:00
committed by SoftFever
parent 6b3e4f18e1
commit 6b45fa2c97
3 changed files with 89 additions and 45 deletions

View File

@@ -2261,11 +2261,17 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
dir = (boost::filesystem::path(data_dir())).make_preferred(); dir = (boost::filesystem::path(data_dir())).make_preferred();
// Try loading from binary cache first (skips JSON parsing on cache hit). // Try loading from binary cache first (skips JSON parsing on cache hit).
// partial_dirty_vendors is non-empty when some vendors changed but others are still cached.
std::set<std::string> partial_dirty_vendors;
if (!validation_mode) { if (!validation_mode) {
const auto t0 = std::chrono::steady_clock::now(); const auto t0 = std::chrono::steady_clock::now();
PresetBundleCache::SystemPresetsCache cache; PresetBundleCache::SystemPresetsCache cache;
const std::string cache_file = PresetBundleCache::SystemPresetsCache::cache_path(); const std::string cache_file = PresetBundleCache::SystemPresetsCache::cache_path();
if (cache.load(cache_file) && cache.is_valid(dir.string())) { if (cache.load(cache_file)) {
std::set<std::string> dirty;
if (cache.get_dirty_vendors(dir.string(), dirty)) {
if (dirty.empty()) {
// Full hit — every vendor is unchanged.
cache.apply(*this); cache.apply(*this);
update_system_maps(); update_system_maps();
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -2273,9 +2279,17 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from cache in " << ms << " ms"; BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from cache in " << ms << " ms";
return {PresetsConfigSubstitutions{}, ""}; return {PresetsConfigSubstitutions{}, ""};
} }
// Partial hit — restore clean vendors from cache, re-parse only dirty ones.
BOOST_LOG_TRIVIAL(info) << "PresetBundle: partial cache hit, " << dirty.size()
<< " vendor(s) changed — re-parsing those only";
cache.apply_partial(*this, dirty);
partial_dirty_vendors = std::move(dirty);
}
// else: structural mismatch (format version or option count changed) → full re-parse.
}
// Try bundled cache shipped with the installer (first launch, before user cache exists). if (partial_dirty_vendors.empty()) {
// Validates against resources/profiles/ — the same directory it was generated from in CI. // No partial hit — try bundled cache shipped with the installer (first launch).
{ {
const std::string bundled_dir = const std::string bundled_dir =
(boost::filesystem::path(resources_dir()) / "profiles").make_preferred().string(); (boost::filesystem::path(resources_dir()) / "profiles").make_preferred().string();
@@ -2292,14 +2306,14 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
return {PresetsConfigSubstitutions{}, ""}; return {PresetsConfigSubstitutions{}, ""};
} }
} }
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " cache miss, falling back to JSON load"; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " cache miss, falling back to JSON load";
} }
}
const auto json_load_t0 = std::chrono::steady_clock::now(); const auto json_load_t0 = std::chrono::steady_clock::now();
PresetsConfigSubstitutions substitutions; PresetsConfigSubstitutions substitutions;
std::string errors_cummulative; std::string errors_cummulative;
bool first = true; bool first = partial_dirty_vendors.empty(); // false in partial mode: clean vendors already applied
std::vector<std::string> vendor_names; std::vector<std::string> vendor_names;
// store all vendor names in vendor_names // store all vendor names in vendor_names
for (auto& dir_entry : boost::filesystem::directory_iterator(dir)) { for (auto& dir_entry : boost::filesystem::directory_iterator(dir)) {
@@ -2321,6 +2335,9 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
std::vector<std::string> other_vendors; std::vector<std::string> other_vendors;
other_vendors.reserve(vendor_names.size()); other_vendors.reserve(vendor_names.size());
for (auto& vn : vendor_names) { for (auto& vn : vendor_names) {
// In partial mode, skip vendors already loaded from cache.
if (!partial_dirty_vendors.empty() && !partial_dirty_vendors.count(vn))
continue;
if (vn == ORCA_FILAMENT_LIBRARY) if (vn == ORCA_FILAMENT_LIBRARY)
orca_lib_vendor = vn; orca_lib_vendor = vn;
else if (!(validation_mode && !vendor_to_validate.empty() && vn != vendor_to_validate)) else if (!(validation_mode && !vendor_to_validate.empty() && vn != vendor_to_validate))

View File

@@ -124,9 +124,14 @@ std::string SystemPresetsCache::bundled_cache_path()
bool SystemPresetsCache::is_valid(const std::string& system_dir) const bool SystemPresetsCache::is_valid(const std::string& system_dir) const
{ {
if (format_version != FORMAT_VERSION) std::set<std::string> dummy;
return false; return get_dirty_vendors(system_dir, dummy) && dummy.empty();
if (config_options_count != print_config_def.options.size()) }
bool SystemPresetsCache::get_dirty_vendors(const std::string& system_dir, std::set<std::string>& out_dirty) const
{
out_dirty.clear();
if (format_version != FORMAT_VERSION || config_options_count != print_config_def.options.size())
return false; return false;
std::map<std::string, std::string> current; std::map<std::string, std::string> current;
try { try {
@@ -143,12 +148,15 @@ bool SystemPresetsCache::is_valid(const std::string& system_dir) const
BOOST_LOG_TRIVIAL(warning) << "PresetBundleCache: directory scan failed: " << e.what(); BOOST_LOG_TRIVIAL(warning) << "PresetBundleCache: directory scan failed: " << e.what();
return false; return false;
} }
if (current.size() != vendor_versions.size()) // A vendor was removed from disk — safest to force a full re-parse.
for (const auto& [name, ver] : vendor_versions)
if (current.find(name) == current.end())
return false; return false;
// Collect vendors with changed or new version strings.
for (const auto& [name, ver] : current) { for (const auto& [name, ver] : current) {
auto it = vendor_versions.find(name); auto it = vendor_versions.find(name);
if (it == vendor_versions.end() || it->second != ver) if (it == vendor_versions.end() || it->second != ver)
return false; out_dirty.insert(name);
} }
return true; return true;
} }
@@ -234,9 +242,16 @@ void SystemPresetsCache::capture(const PresetBundle& bundle, const std::string&
} }
void SystemPresetsCache::apply(PresetBundle& bundle) const void SystemPresetsCache::apply(PresetBundle& bundle) const
{
apply_partial(bundle, {});
}
void SystemPresetsCache::apply_partial(PresetBundle& bundle, const std::set<std::string>& skip_vendor_ids) const
{ {
bundle.reset(false); bundle.reset(false);
for (const auto& cvp : vendor_profiles) { for (const auto& cvp : vendor_profiles) {
if (skip_vendor_ids.count(cvp.id))
continue;
VendorProfile vp(cvp.id); VendorProfile vp(cvp.id);
vp.name = cvp.name; vp.name = cvp.name;
vp.config_update_url = cvp.config_update_url; vp.config_update_url = cvp.config_update_url;
@@ -273,10 +288,12 @@ void SystemPresetsCache::apply(PresetBundle& bundle) const
bundle.vendors.emplace(cvp.id, std::move(vp)); bundle.vendors.emplace(cvp.id, std::move(vp));
} }
auto apply_col = [&bundle](const std::vector<CachedPreset>& cached, auto apply_col = [&](const std::vector<CachedPreset>& cached,
PresetCollection& coll, PresetCollection& coll,
bool is_filaments) { bool is_filaments) {
for (const auto& cp : cached) { for (const auto& cp : cached) {
if (skip_vendor_ids.count(cp.vendor_id))
continue;
Semver version; Semver version;
if (!cp.version.empty()) { if (!cp.version.empty()) {
auto v = Semver::parse(cp.version); auto v = Semver::parse(cp.version);
@@ -307,9 +324,12 @@ void SystemPresetsCache::apply(PresetBundle& bundle) const
apply_col(sla_print_presets, bundle.sla_prints, false); apply_col(sla_print_presets, bundle.sla_prints, false);
apply_col(sla_material_presets, bundle.sla_materials, false); apply_col(sla_material_presets, bundle.sla_materials, false);
// config_maps and filament_id_maps are derived from ORCA_FILAMENT_LIBRARY.
// If that vendor is in skip_vendor_ids it will be re-loaded from JSON and will
// overwrite these; otherwise the cached values are still valid.
bundle.m_config_maps = config_maps; bundle.m_config_maps = config_maps;
bundle.m_filament_id_maps = filament_id_maps; bundle.m_filament_id_maps = filament_id_maps;
// Caller must invoke bundle.update_system_maps() after this (it is private to PresetBundle). // Caller must invoke bundle.update_system_maps() after all loading is done.
} }
bool SystemPresetsCache::load(const std::string& path) bool SystemPresetsCache::load(const std::string& path)

View File

@@ -1,7 +1,8 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <map> #include <map>
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -121,8 +122,14 @@ struct SystemPresetsCache {
static std::string cache_path(); static std::string cache_path();
static std::string bundled_cache_path(); static std::string bundled_cache_path();
bool is_valid(const std::string& system_dir) const; bool is_valid(const std::string& system_dir) const;
// Returns false on structural mismatch (full re-parse required).
// On true, populates out_dirty with vendor IDs whose version strings changed.
// Empty out_dirty means fully valid (cache hit, no re-parse needed).
bool get_dirty_vendors(const std::string& system_dir, std::set<std::string>& out_dirty) const;
void capture(const PresetBundle& bundle, const std::string& system_dir); void capture(const PresetBundle& bundle, const std::string& system_dir);
void apply(PresetBundle& bundle) const; void apply(PresetBundle& bundle) const;
// Same as apply() but skips vendors listed in skip_vendor_ids and their presets.
void apply_partial(PresetBundle& bundle, const std::set<std::string>& skip_vendor_ids) const;
bool load(const std::string& path); bool load(const std::string& path);
void save(const std::string& path) const; void save(const std::string& path) const;
}; };