From 88e3e59c645a4f3031e562d9309a80ecc31939c1 Mon Sep 17 00:00:00 2001 From: Andrew <159703254+andrewsoonqn@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:28:26 +0800 Subject: [PATCH] Convert actions storage to unordered_map Enhance action lookup efficiency by switching from a vector to an unordered_map, allowing faster access by action ID. --- src/slic3r/GUI/ActionRegistry.cpp | 43 +++++++++++++------------------ src/slic3r/GUI/ActionRegistry.hpp | 10 ++----- 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/src/slic3r/GUI/ActionRegistry.cpp b/src/slic3r/GUI/ActionRegistry.cpp index d1b5b4b66b..270f1b03e5 100644 --- a/src/slic3r/GUI/ActionRegistry.cpp +++ b/src/slic3r/GUI/ActionRegistry.cpp @@ -75,18 +75,14 @@ void ActionRegistry::upsert(std::shared_ptr action) return; seed_state(*action); - auto existing = std::find_if(m_actions.begin(), m_actions.end(), - [&](const auto& current) { return current->id == action->id; }); - if (existing == m_actions.end()) - m_actions.push_back(std::move(action)); - else - *existing = std::move(action); + std::string id = action->id; + m_actions.insert_or_assign(std::move(id), std::move(action)); } void ActionRegistry::remove(const std::string& id) { assert(wxThread::IsMain()); - erase_id(id); + m_actions.erase(id); } void ActionRegistry::seed_state(AppAction& a) const @@ -110,8 +106,8 @@ void ActionRegistry::seed_state(AppAction& a) const const AppAction* ActionRegistry::by_id(const std::string& id) const { assert(wxThread::IsMain()); - auto it = std::find_if(m_actions.begin(), m_actions.end(), [&](const auto& a) { return a->id == id; }); - return it == m_actions.end() ? nullptr : it->get(); + auto it = m_actions.find(id); + return it == m_actions.end() ? nullptr : it->second.get(); } AppAction* ActionRegistry::find(const std::string& id) @@ -119,25 +115,18 @@ AppAction* ActionRegistry::find(const std::string& id) return const_cast(by_id(id)); } -void ActionRegistry::erase_id(const std::string& id) -{ - m_actions.erase(std::remove_if(m_actions.begin(), m_actions.end(), - [&](const auto& a) { return a->id == id; }), - m_actions.end()); -} - // ---- dispatch + write-through ---------------------------------------------- AppActionRunResult ActionRegistry::run(const std::string& id) { assert(wxThread::IsMain()); - auto it = std::find_if(m_actions.begin(), m_actions.end(), [&](const auto& e) { return e->id == id; }); + auto it = m_actions.find(id); if (it == m_actions.end()) return {}; // default Info, empty message - // why: hold a shared_ptr keep-alive, never a bare vector element. A runner may pump - // a nested event loop; a queued source refresh can erase this action from m_actions, - // while the keep-alive preserves the object until run returns. - std::shared_ptr keep = *it; + // why: hold a shared_ptr keep-alive, never a bare map entry. A runner may pump a + // nested event loop; a queued source refresh can erase the entry while the + // keep-alive preserves the action until run returns. + std::shared_ptr keep = it->second; AppActionRunResult o = keep->run(); if (o.level == AppActionRunResult::Level::Busy) return o; @@ -210,16 +199,20 @@ nlohmann::json ActionRegistry::snapshot() const assert(wxThread::IsMain()); std::vector sorted; sorted.reserve(m_actions.size()); - for (const auto& a : m_actions) - sorted.push_back(a.get()); + for (const auto& entry : m_actions) + sorted.push_back(entry.second.get()); const long long now = (long long) std::time(nullptr); - std::stable_sort(sorted.begin(), sorted.end(), [&](const AppAction* a, const AppAction* b) { + std::sort(sorted.begin(), sorted.end(), [&](const AppAction* a, const AppAction* b) { double sa = frecency_score(a->count, a->last, now); double sb = frecency_score(b->count, b->last, now); if (sa != sb) return sa > sb; - return a->title < b->title; // ties alphabetical + if (a->title != b->title) + return a->title < b->title; + if (a->source != b->source) + return a->source < b->source; + return a->id < b->id; }); nlohmann::json actions = nlohmann::json::array(); diff --git a/src/slic3r/GUI/ActionRegistry.hpp b/src/slic3r/GUI/ActionRegistry.hpp index e6f6dd65cf..c76f6bee44 100644 --- a/src/slic3r/GUI/ActionRegistry.hpp +++ b/src/slic3r/GUI/ActionRegistry.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -81,7 +82,6 @@ public: void remove(const std::string& id); // Always-clean read surface. UI thread only. - const std::vector>& all() const { assert(wxThread::IsMain()); return m_actions; } const AppAction* by_id(const std::string& id) const; // Dispatch + write-through (registry is the only thing that touches AppConfig). @@ -99,15 +99,9 @@ public: private: void seed_state(AppAction& a) const; // favourite/stats from config AppAction* find(const std::string& id); - void erase_id(const std::string& id); - // why: vector storage favors the popup path, which snapshots by iterating, sorting, and - // serializing small action sets. Id lookups are event-driven and initialization is startup-only, so - // a persistent map would add hashing/allocation cost without helping render. - // shared_ptr: AppAction is abstract (stored by pointer), and run() takes a keep-alive - // copy so a queued source refresh cannot erase the object mid-run. std::vector> m_sources; - std::vector> m_actions; // UI-thread confined; no lock + std::unordered_map> m_actions; // UI-thread confined; no lock }; }} // namespace Slic3r::GUI