From fe03dfaa3bb32622139a1a3b124d31af631e7837 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 2 Mar 2026 22:40:14 -0600 Subject: [PATCH] perf(memory): eliminate deep copies in range-based for loops and lambdas This commit addresses memory churn caused by implicit deep copies during traversal and allocation of complex structures: - Replaced pass-by-value 'Json::Value' in std::ranges and range-based for loops with 'const auto&' or 'const Json::Value&' in Hyprland modules, preventing large JSON tree duplications on every update. - Fixed implicit string and pair copies in UPower and CPU Frequency loops by converting 'auto' to 'const auto&' where possible. - Added 'std::vector::reserve' calls before 'push_back' loops in MPRIS, Niri, and CFFI modules to prevent exponential vector reallocation during initialization. Signed-off-by: Austin Horstman --- src/modules/cffi.cpp | 1 + src/modules/cpu_frequency/linux.cpp | 2 +- src/modules/hyprland/window.cpp | 16 ++++++++-------- src/modules/hyprland/windowcount.cpp | 4 ++-- src/modules/hyprland/workspace.cpp | 2 +- src/modules/hyprland/workspaces.cpp | 6 +++--- src/modules/mpris/mpris.cpp | 1 + src/modules/niri/language.cpp | 1 + 8 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/modules/cffi.cpp b/src/modules/cffi.cpp index 930c4d47..c3f1398f 100644 --- a/src/modules/cffi.cpp +++ b/src/modules/cffi.cpp @@ -71,6 +71,7 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co // Prepare config_entries array std::vector config_entries; + config_entries.reserve(keys.size()); for (size_t i = 0; i < keys.size(); i++) { config_entries.push_back({keys[i].c_str(), config_entries_stringstor[i].c_str()}); } diff --git a/src/modules/cpu_frequency/linux.cpp b/src/modules/cpu_frequency/linux.cpp index 1f368789..83f06aa5 100644 --- a/src/modules/cpu_frequency/linux.cpp +++ b/src/modules/cpu_frequency/linux.cpp @@ -26,7 +26,7 @@ std::vector waybar::modules::CpuFrequency::parseCpuFrequencies() { if (std::filesystem::exists(cpufreq_dir)) { std::vector frequency_files = {"/cpuinfo_min_freq", "/cpuinfo_max_freq"}; for (auto& p : std::filesystem::directory_iterator(cpufreq_dir)) { - for (auto freq_file : frequency_files) { + for (const auto& freq_file : frequency_files) { std::string freq_file_path = p.path().string() + freq_file; if (std::filesystem::exists(freq_file_path)) { std::string freq_value; diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 1fddb45b..97d49eaf 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -124,7 +124,7 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { const auto monitors = IPC::inst().getSocket1JsonReply("monitors"); if (monitors.isArray()) { auto monitor = std::ranges::find_if( - monitors, [&](Json::Value monitor) { return monitor["name"] == monitorName; }); + monitors, [&](const Json::Value& monitor) { return monitor["name"] == monitorName; }); if (monitor == std::end(monitors)) { spdlog::warn("Monitor not found: {}", monitorName); return Workspace{ @@ -139,7 +139,7 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { const auto workspaces = IPC::inst().getSocket1JsonReply("workspaces"); if (workspaces.isArray()) { auto workspace = std::ranges::find_if( - workspaces, [&](Json::Value workspace) { return workspace["id"] == id; }); + workspaces, [&](const Json::Value& workspace) { return workspace["id"] == id; }); if (workspace == std::end(workspaces)) { spdlog::warn("No workspace with id {}", id); return Workspace{ @@ -190,7 +190,7 @@ void Window::queryActiveWorkspace() { const auto clients = m_ipc.getSocket1JsonReply("clients"); if (clients.isArray()) { auto activeWindow = std::ranges::find_if( - clients, [&](Json::Value window) { return window["address"] == workspace_.last_window; }); + clients, [&](const Json::Value& window) { return window["address"] == workspace_.last_window; }); if (activeWindow == std::end(clients)) { focused_ = false; @@ -200,19 +200,19 @@ void Window::queryActiveWorkspace() { windowData_ = WindowData::parse(*activeWindow); updateAppIconName(windowData_.class_name, windowData_.initial_class_name); std::vector workspaceWindows; - std::ranges::copy_if(clients, std::back_inserter(workspaceWindows), [&](Json::Value window) { + std::ranges::copy_if(clients, std::back_inserter(workspaceWindows), [&](const Json::Value& window) { return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); }); - swallowing_ = std::ranges::any_of(workspaceWindows, [&](Json::Value window) { + swallowing_ = std::ranges::any_of(workspaceWindows, [&](const Json::Value& window) { return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; }); std::vector visibleWindows; std::ranges::copy_if(workspaceWindows, std::back_inserter(visibleWindows), - [&](Json::Value window) { return !window["hidden"].asBool(); }); + [&](const Json::Value& window) { return !window["hidden"].asBool(); }); solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), - [&](Json::Value window) { return !window["floating"].asBool(); }); + [&](const Json::Value& window) { return !window["floating"].asBool(); }); allFloating_ = std::ranges::all_of( - visibleWindows, [&](Json::Value window) { return window["floating"].asBool(); }); + visibleWindows, [&](const Json::Value& window) { return window["floating"].asBool(); }); fullscreen_ = windowData_.fullscreen; // Fullscreen windows look like they are solo diff --git a/src/modules/hyprland/windowcount.cpp b/src/modules/hyprland/windowcount.cpp index ab573cca..5f5db80c 100644 --- a/src/modules/hyprland/windowcount.cpp +++ b/src/modules/hyprland/windowcount.cpp @@ -79,7 +79,7 @@ auto WindowCount::getActiveWorkspace(const std::string& monitorName) -> Workspac const auto monitors = m_ipc.getSocket1JsonReply("monitors"); if (monitors.isArray()) { auto monitor = std::ranges::find_if( - monitors, [&](Json::Value monitor) { return monitor["name"] == monitorName; }); + monitors, [&](const Json::Value& monitor) { return monitor["name"] == monitorName; }); if (monitor == std::end(monitors)) { spdlog::warn("Monitor not found: {}", monitorName); return Workspace{ @@ -93,7 +93,7 @@ auto WindowCount::getActiveWorkspace(const std::string& monitorName) -> Workspac const auto workspaces = m_ipc.getSocket1JsonReply("workspaces"); if (workspaces.isArray()) { auto workspace = std::ranges::find_if( - workspaces, [&](Json::Value workspace) { return workspace["id"] == id; }); + workspaces, [&](const Json::Value& workspace) { return workspace["id"] == id; }); if (workspace == std::end(workspaces)) { spdlog::warn("No workspace with id {}", id); return Workspace{ diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index 87933ac0..21e7ef9b 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -96,7 +96,7 @@ bool Workspace::handleClicked(GdkEventButton* bt) const { void Workspace::initializeWindowMap(const Json::Value& clients_data) { m_windowMap.clear(); - for (auto client : clients_data) { + for (const auto& client : clients_data) { if (client["workspace"]["id"].asInt() == id()) { insertWindow({client}); } diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index dd891673..cbd14f90 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -197,7 +197,7 @@ void Workspaces::initializeWorkspaces() { auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces"); auto const clientsJson = m_ipc.getSocket1JsonReply("clients"); - for (Json::Value workspaceJson : workspacesJson) { + for (const auto& workspaceJson : workspacesJson) { std::string workspaceName = workspaceJson["name"].asString(); if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && (!workspaceName.starts_with("special") || showSpecial()) && @@ -401,7 +401,7 @@ void Workspaces::onWorkspaceCreated(std::string const& payload, Json::Value cons auto const workspaceRules = m_ipc.getSocket1JsonReply("workspacerules"); auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces"); - for (Json::Value workspaceJson : workspacesJson) { + for (auto workspaceJson : workspacesJson) { const auto currentId = workspaceJson["id"].asInt(); if (currentId == *workspaceId) { std::string workspaceName = workspaceJson["name"].asString(); @@ -1004,7 +1004,7 @@ void Workspaces::setUrgentWorkspace(std::string const& windowaddress) { const Json::Value clientsJson = m_ipc.getSocket1JsonReply("clients"); int workspaceId = -1; - for (Json::Value clientJson : clientsJson) { + for (const auto& clientJson : clientsJson) { if (clientJson["address"].asString().ends_with(windowaddress)) { workspaceId = clientJson["workspace"]["id"].asInt(); break; diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 1bdd7df6..93f73690 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -109,6 +109,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) player_ = config_["player"].asString(); } if (config_["ignored-players"].isArray()) { + ignored_players_.reserve(config_["ignored-players"].size()); for (const auto& item : config_["ignored-players"]) { if (item.isString()) { ignored_players_.push_back(item.asString()); diff --git a/src/modules/niri/language.cpp b/src/modules/niri/language.cpp index 3359ef4c..14ca555f 100644 --- a/src/modules/niri/language.cpp +++ b/src/modules/niri/language.cpp @@ -32,6 +32,7 @@ void Language::updateFromIPC() { auto ipcLock = gIPC->lockData(); layouts_.clear(); + layouts_.reserve(gIPC->keyboardLayoutNames().size()); for (const auto& fullName : gIPC->keyboardLayoutNames()) layouts_.push_back(getLayout(fullName)); current_idx_ = gIPC->keyboardLayoutCurrent();