From fa26333a5bd02d70c411b402cedc7e69167b3862 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 29 Jun 2022 16:55:40 +0800 Subject: [PATCH 1/2] Move "memory priority" settings to Application class --- src/app/application.cpp | 63 +++++++++++++++++++++++++++ src/app/application.h | 10 +++++ src/app/upgrade.cpp | 23 +++++++++- src/base/bittorrent/session.cpp | 68 ------------------------------ src/base/bittorrent/session.h | 23 ---------- src/base/interfaces/iapplication.h | 25 +++++++++++ src/gui/advancedsettings.cpp | 14 +++--- 7 files changed, 127 insertions(+), 99 deletions(-) diff --git a/src/app/application.cpp b/src/app/application.cpp index 5b32738f2..83aa0f071 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -129,6 +129,9 @@ Application::Application(int &argc, char **argv) , m_storeFileLoggerAgeType(FILELOGGER_SETTINGS_KEY(u"AgeType"_qs)) , m_storeFileLoggerPath(FILELOGGER_SETTINGS_KEY(u"Path"_qs)) , m_storeMemoryWorkingSetLimit(SETTINGS_KEY(u"MemoryWorkingSetLimit"_qs)) +#ifdef Q_OS_WIN + , m_processMemoryPriority(SETTINGS_KEY(u"ProcessMemoryPriority"_qs)) +#endif { qRegisterMetaType("Log::Msg"); qRegisterMetaType("Log::Peer"); @@ -607,6 +610,7 @@ int Application::exec(const QStringList ¶ms) #endif #ifdef Q_OS_WIN + applyMemoryPriority(); adjustThreadPriority(); #endif @@ -822,6 +826,65 @@ void Application::applyMemoryWorkingSetLimit() const #endif #ifdef Q_OS_WIN +MemoryPriority Application::processMemoryPriority() const +{ + return m_processMemoryPriority.get(MemoryPriority::BelowNormal); +} + +void Application::setProcessMemoryPriority(const MemoryPriority priority) +{ + if (processMemoryPriority() == priority) + return; + + m_processMemoryPriority = priority; + applyMemoryPriority(); +} + +void Application::applyMemoryPriority() const +{ + using SETPROCESSINFORMATION = BOOL (WINAPI *)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD); + const auto setProcessInformation = Utils::Misc::loadWinAPI(u"Kernel32.dll"_qs, "SetProcessInformation"); + if (!setProcessInformation) // only available on Windows >= 8 + return; + +#if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + // this dummy struct is required to compile successfully when targeting older Windows version + struct MEMORY_PRIORITY_INFORMATION + { + ULONG MemoryPriority; + }; + +#define MEMORY_PRIORITY_LOWEST 0 +#define MEMORY_PRIORITY_VERY_LOW 1 +#define MEMORY_PRIORITY_LOW 2 +#define MEMORY_PRIORITY_MEDIUM 3 +#define MEMORY_PRIORITY_BELOW_NORMAL 4 +#define MEMORY_PRIORITY_NORMAL 5 +#endif + + MEMORY_PRIORITY_INFORMATION prioInfo {}; + switch (processMemoryPriority()) + { + case MemoryPriority::Normal: + default: + prioInfo.MemoryPriority = MEMORY_PRIORITY_NORMAL; + break; + case MemoryPriority::BelowNormal: + prioInfo.MemoryPriority = MEMORY_PRIORITY_BELOW_NORMAL; + break; + case MemoryPriority::Medium: + prioInfo.MemoryPriority = MEMORY_PRIORITY_MEDIUM; + break; + case MemoryPriority::Low: + prioInfo.MemoryPriority = MEMORY_PRIORITY_LOW; + break; + case MemoryPriority::VeryLow: + prioInfo.MemoryPriority = MEMORY_PRIORITY_VERY_LOW; + break; + } + setProcessInformation(::GetCurrentProcess(), ProcessMemoryPriority, &prioInfo, sizeof(prioInfo)); +} + void Application::adjustThreadPriority() const { // Workaround for improving responsiveness of qbt when CPU resources are scarce. diff --git a/src/app/application.h b/src/app/application.h index bf6ba52c1..162cab62d 100644 --- a/src/app/application.h +++ b/src/app/application.h @@ -118,6 +118,11 @@ public: int memoryWorkingSetLimit() const override; void setMemoryWorkingSetLimit(int size) override; +#ifdef Q_OS_WIN + MemoryPriority processMemoryPriority() const override; + void setProcessMemoryPriority(MemoryPriority priority) override; +#endif + #ifndef DISABLE_GUI MainWindow *mainWindow() override; #endif @@ -151,6 +156,7 @@ private: #endif #ifdef Q_OS_WIN + void applyMemoryPriority() const; void adjustThreadPriority() const; #endif @@ -183,6 +189,10 @@ private: SettingValue m_storeFileLoggerPath; SettingValue m_storeMemoryWorkingSetLimit; +#ifdef Q_OS_WIN + SettingValue m_processMemoryPriority; +#endif + #ifndef DISABLE_GUI MainWindow *m_window = nullptr; #endif diff --git a/src/app/upgrade.cpp b/src/app/upgrade.cpp index 1121f43be..9a22515c5 100644 --- a/src/app/upgrade.cpp +++ b/src/app/upgrade.cpp @@ -28,6 +28,7 @@ #include "upgrade.h" +#include #include #include "base/bittorrent/torrentcontentlayout.h" @@ -44,7 +45,7 @@ namespace { - const int MIGRATION_VERSION = 3; + const int MIGRATION_VERSION = 4; const QString MIGRATION_VERSION_KEY = u"Meta/MigrationVersion"_qs; void exportWebUIHttpsFiles() @@ -368,6 +369,21 @@ namespace } } } + +#ifdef Q_OS_WIN + void migrateMemoryPrioritySettings() + { + auto *settingsStorage = SettingsStorage::instance(); + const QString oldKey = u"BitTorrent/OSMemoryPriority"_qs; + const QString newKey = u"Application/ProcessMemoryPriority"_qs; + + if (settingsStorage->hasKey(oldKey)) + { + const auto value = settingsStorage->loadValue(oldKey); + settingsStorage->storeValue(newKey, value); + } + } +#endif } bool upgrade(const bool /*ask*/) @@ -392,6 +408,11 @@ bool upgrade(const bool /*ask*/) if (version < 3) migrateProxySettingsEnum(); +#ifdef Q_OS_WIN + if (version < 4) + migrateMemoryPrioritySettings(); +#endif + version = MIGRATION_VERSION; } diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index 46e7f9d0f..a90199711 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -447,9 +447,6 @@ Session::Session(QObject *parent) } ) , m_resumeDataStorageType(BITTORRENT_SESSION_KEY(u"ResumeDataStorageType"_qs), ResumeDataStorageType::Legacy) -#if defined(Q_OS_WIN) - , m_OSMemoryPriority(BITTORRENT_KEY(u"OSMemoryPriority"_qs), OSMemoryPriority::BelowNormal) -#endif , m_seedingLimitTimer {new QTimer {this}} , m_resumeDataTimer {new QTimer {this}} , m_statistics {new Statistics {this}} @@ -1067,10 +1064,6 @@ void Session::configureComponents() disableIPFilter(); m_IPFilteringConfigured = true; } - -#if defined(Q_OS_WIN) - applyOSMemoryPriority(); -#endif } void Session::prepareStartup() @@ -3494,67 +3487,6 @@ bool Session::isRestored() const return m_isRestored; } -#if defined(Q_OS_WIN) -OSMemoryPriority Session::getOSMemoryPriority() const -{ - return m_OSMemoryPriority; -} - -void Session::setOSMemoryPriority(const OSMemoryPriority priority) -{ - if (m_OSMemoryPriority == priority) - return; - - m_OSMemoryPriority = priority; - configureDeferred(); -} - -void Session::applyOSMemoryPriority() const -{ - using SETPROCESSINFORMATION = BOOL (WINAPI *)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD); - const auto setProcessInformation = Utils::Misc::loadWinAPI(u"Kernel32.dll"_qs, "SetProcessInformation"); - if (!setProcessInformation) // only available on Windows >= 8 - return; - -#if (_WIN32_WINNT < _WIN32_WINNT_WIN8) - // this dummy struct is required to compile successfully when targeting older Windows version - struct MEMORY_PRIORITY_INFORMATION - { - ULONG MemoryPriority; - }; - -#define MEMORY_PRIORITY_LOWEST 0 -#define MEMORY_PRIORITY_VERY_LOW 1 -#define MEMORY_PRIORITY_LOW 2 -#define MEMORY_PRIORITY_MEDIUM 3 -#define MEMORY_PRIORITY_BELOW_NORMAL 4 -#define MEMORY_PRIORITY_NORMAL 5 -#endif - - MEMORY_PRIORITY_INFORMATION prioInfo {}; - switch (getOSMemoryPriority()) - { - case OSMemoryPriority::Normal: - default: - prioInfo.MemoryPriority = MEMORY_PRIORITY_NORMAL; - break; - case OSMemoryPriority::BelowNormal: - prioInfo.MemoryPriority = MEMORY_PRIORITY_BELOW_NORMAL; - break; - case OSMemoryPriority::Medium: - prioInfo.MemoryPriority = MEMORY_PRIORITY_MEDIUM; - break; - case OSMemoryPriority::Low: - prioInfo.MemoryPriority = MEMORY_PRIORITY_LOW; - break; - case OSMemoryPriority::VeryLow: - prioInfo.MemoryPriority = MEMORY_PRIORITY_VERY_LOW; - break; - } - setProcessInformation(::GetCurrentProcess(), ProcessMemoryPriority, &prioInfo, sizeof(prioInfo)); -} -#endif - int Session::maxConnectionsPerTorrent() const { return m_maxConnectionsPerTorrent; diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 6b588504d..dd2ff6c62 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -29,7 +29,6 @@ #pragma once -#include #include #include @@ -151,18 +150,6 @@ namespace BitTorrent SQLite }; Q_ENUM_NS(ResumeDataStorageType) - -#if defined(Q_OS_WIN) - enum class OSMemoryPriority : int - { - Normal = 0, - BelowNormal = 1, - Medium = 2, - Low = 3, - VeryLow = 4 - }; - Q_ENUM_NS(OSMemoryPriority) -#endif } struct SessionMetricIndices @@ -464,10 +451,6 @@ namespace BitTorrent void setBannedIPs(const QStringList &newList); ResumeDataStorageType resumeDataStorageType() const; void setResumeDataStorageType(ResumeDataStorageType type); -#if defined(Q_OS_WIN) - OSMemoryPriority getOSMemoryPriority() const; - void setOSMemoryPriority(OSMemoryPriority priority); -#endif bool isRestored() const; @@ -629,9 +612,6 @@ namespace BitTorrent void populateAdditionalTrackers(); void enableIPFilter(); void disableIPFilter(); -#if defined(Q_OS_WIN) - void applyOSMemoryPriority() const; -#endif void processTrackerStatuses(); void populateExcludedFileNamesRegExpList(); void prepareStartup(); @@ -795,9 +775,6 @@ namespace BitTorrent CachedSettingValue m_excludedFileNames; CachedSettingValue m_bannedIPs; CachedSettingValue m_resumeDataStorageType; -#if defined(Q_OS_WIN) - CachedSettingValue m_OSMemoryPriority; -#endif bool m_isRestored = false; diff --git a/src/base/interfaces/iapplication.h b/src/base/interfaces/iapplication.h index 94cdbdb43..3942fd9d0 100644 --- a/src/base/interfaces/iapplication.h +++ b/src/base/interfaces/iapplication.h @@ -30,11 +30,31 @@ #pragma once +#include +#include + class QString; class Path; struct QBtCommandLineParameters; +#ifdef Q_OS_WIN +inline namespace ApplicationSettingsEnums +{ + Q_NAMESPACE + + enum class MemoryPriority : int + { + Normal = 0, + BelowNormal = 1, + Medium = 2, + Low = 3, + VeryLow = 4 + }; + Q_ENUM_NS(MemoryPriority) +} +#endif + class IApplication { public: @@ -58,4 +78,9 @@ public: virtual int memoryWorkingSetLimit() const = 0; virtual void setMemoryWorkingSetLimit(int size) = 0; + +#ifdef Q_OS_WIN + virtual MemoryPriority processMemoryPriority() const = 0; + virtual void setProcessMemoryPriority(MemoryPriority priority) = 0; +#endif }; diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp index 64cfd1009..4a1201c6b 100644 --- a/src/gui/advancedsettings.cpp +++ b/src/gui/advancedsettings.cpp @@ -185,7 +185,7 @@ void AdvancedSettings::saveAdvancedSettings() const app()->setMemoryWorkingSetLimit(m_spinBoxMemoryWorkingSetLimit.value()); #endif #if defined(Q_OS_WIN) - session->setOSMemoryPriority(m_comboBoxOSMemoryPriority.currentData().value()); + app()->setProcessMemoryPriority(m_comboBoxOSMemoryPriority.currentData().value()); #endif // Async IO threads session->setAsyncIOThreads(m_spinBoxAsyncIOThreads.value()); @@ -422,12 +422,12 @@ void AdvancedSettings::loadAdvancedSettings() , &m_spinBoxMemoryWorkingSetLimit); #endif #if defined(Q_OS_WIN) - m_comboBoxOSMemoryPriority.addItem(tr("Normal"), QVariant::fromValue(BitTorrent::OSMemoryPriority::Normal)); - m_comboBoxOSMemoryPriority.addItem(tr("Below normal"), QVariant::fromValue(BitTorrent::OSMemoryPriority::BelowNormal)); - m_comboBoxOSMemoryPriority.addItem(tr("Medium"), QVariant::fromValue(BitTorrent::OSMemoryPriority::Medium)); - m_comboBoxOSMemoryPriority.addItem(tr("Low"), QVariant::fromValue(BitTorrent::OSMemoryPriority::Low)); - m_comboBoxOSMemoryPriority.addItem(tr("Very low"), QVariant::fromValue(BitTorrent::OSMemoryPriority::VeryLow)); - m_comboBoxOSMemoryPriority.setCurrentIndex(m_comboBoxOSMemoryPriority.findData(QVariant::fromValue(session->getOSMemoryPriority()))); + m_comboBoxOSMemoryPriority.addItem(tr("Normal"), QVariant::fromValue(MemoryPriority::Normal)); + m_comboBoxOSMemoryPriority.addItem(tr("Below normal"), QVariant::fromValue(MemoryPriority::BelowNormal)); + m_comboBoxOSMemoryPriority.addItem(tr("Medium"), QVariant::fromValue(MemoryPriority::Medium)); + m_comboBoxOSMemoryPriority.addItem(tr("Low"), QVariant::fromValue(MemoryPriority::Low)); + m_comboBoxOSMemoryPriority.addItem(tr("Very low"), QVariant::fromValue(MemoryPriority::VeryLow)); + m_comboBoxOSMemoryPriority.setCurrentIndex(m_comboBoxOSMemoryPriority.findData(QVariant::fromValue(app()->processMemoryPriority()))); addRow(OS_MEMORY_PRIORITY, (tr("Process memory priority (Windows >= 8 only)") + u' ' + makeLink(u"https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-memory_priority_information", u"(?)")) , &m_comboBoxOSMemoryPriority); From ed2eadaf56ae23e3d481fbbf028b345f30b583ee Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Sat, 2 Jul 2022 19:05:15 +0800 Subject: [PATCH 2/2] Work around application stuttering on Windows This is observed by unusual high page faults when the stuttering occurs. With this workaround, the high page faults still occurs but the GUI remains responsive. --- src/app/application.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/app/application.cpp b/src/app/application.cpp index 83aa0f071..45e6dc43a 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -847,6 +847,11 @@ void Application::applyMemoryPriority() const if (!setProcessInformation) // only available on Windows >= 8 return; + using SETTHREADINFORMATION = BOOL (WINAPI *)(HANDLE, THREAD_INFORMATION_CLASS, LPVOID, DWORD); + const auto setThreadInformation = Utils::Misc::loadWinAPI(u"Kernel32.dll"_qs, "SetThreadInformation"); + if (!setThreadInformation) // only available on Windows >= 8 + return; + #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) // this dummy struct is required to compile successfully when targeting older Windows version struct MEMORY_PRIORITY_INFORMATION @@ -883,6 +888,11 @@ void Application::applyMemoryPriority() const break; } setProcessInformation(::GetCurrentProcess(), ProcessMemoryPriority, &prioInfo, sizeof(prioInfo)); + + // To avoid thrashing/sluggishness of the app, set "main event loop" thread to normal memory priority + // which is higher/equal than other threads + prioInfo.MemoryPriority = MEMORY_PRIORITY_NORMAL; + setThreadInformation(::GetCurrentThread(), ThreadMemoryPriority, &prioInfo, sizeof(prioInfo)); } void Application::adjustThreadPriority() const