diff options
| author | Stefan Boberg <[email protected]> | 2023-10-12 10:17:17 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-10-12 10:17:17 +0200 |
| commit | 4b97a66d2ea8e75bcf8a93b321514e9050a9ecdd (patch) | |
| tree | 8220079e7124b0b6395b15b9d1ef449e815682a1 /src | |
| parent | added explicit implementation of IoHash equals operator (#464) (diff) | |
| download | zen-4b97a66d2ea8e75bcf8a93b321514e9050a9ecdd.tar.xz zen-4b97a66d2ea8e75bcf8a93b321514e9050a9ecdd.zip | |
adjust resource usage for dedicated servers (#466)
when dedicated mode is enabled via `--dedicated` or `server.dedicated` then we tune http.sys server settings to be more suitable for a shared server
initially we tune two things
* the thread pool used to service I/O requests allows a larger number of threads to be created when needed. The minimum thread count is unchanged but in dedicated server mode we double the maximum number of threads allowed
* the http.sys request queue length (`HttpServerQueueLengthProperty`) is increased to 50,000 in dedicated mode. The regular default is 1,000
Diffstat (limited to 'src')
| -rw-r--r-- | src/zenhttp/httpserver.cpp | 3 | ||||
| -rw-r--r-- | src/zenhttp/httpsys.cpp | 82 | ||||
| -rw-r--r-- | src/zenhttp/httpsys.h | 1 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/httpserver.h | 3 | ||||
| -rw-r--r-- | src/zenhttp/iothreadpool.cpp | 11 | ||||
| -rw-r--r-- | src/zenhttp/iothreadpool.h | 8 | ||||
| -rw-r--r-- | src/zenserver/config.cpp | 2 |
7 files changed, 84 insertions, 26 deletions
diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp index a2ea4cff8..eb13f77fc 100644 --- a/src/zenhttp/httpserver.cpp +++ b/src/zenhttp/httpserver.cpp @@ -792,7 +792,8 @@ CreateHttpServer(const HttpServerConfig& Config) return Ref<HttpServer>(CreateHttpSysServer({.ThreadCount = Config.ThreadCount, .AsyncWorkThreadCount = Config.HttpSys.AsyncWorkThreadCount, .IsAsyncResponseEnabled = Config.HttpSys.IsAsyncResponseEnabled, - .IsRequestLoggingEnabled = Config.HttpSys.IsRequestLoggingEnabled})); + .IsRequestLoggingEnabled = Config.HttpSys.IsRequestLoggingEnabled, + .IsDedicatedServer = Config.IsDedicatedServer})); #endif case HttpServerClass::kHttpNull: diff --git a/src/zenhttp/httpsys.cpp b/src/zenhttp/httpsys.cpp index 8401dcf83..75d8bfe7a 100644 --- a/src/zenhttp/httpsys.cpp +++ b/src/zenhttp/httpsys.cpp @@ -32,7 +32,10 @@ namespace zen { /** * @brief Windows implementation of HTTP server based on http.sys * - * This requires elevation to function + * This requires elevation to function by default but system configuration + * can soften this requirement. + * + * See README.md for details. */ class HttpSysServer : public HttpServer { @@ -76,7 +79,8 @@ private: bool m_IsRequestLoggingEnabled = false; bool m_IsAsyncResponseEnabled = true; - WinIoThreadPool m_ThreadPool; + std::unique_ptr<WinIoThreadPool> m_IoThreadPool; + RwLock m_AsyncWorkPoolInitLock; WorkerThreadPool* m_AsyncWorkPool = nullptr; @@ -89,7 +93,7 @@ private: int32_t m_MinPendingRequests = 16; int32_t m_MaxPendingRequests = 128; Event m_ShutdownEvent; - const HttpSysConfig m_InitialConfig; + HttpSysConfig m_InitialConfig; }; } // namespace zen @@ -889,14 +893,45 @@ HttpAsyncWorkRequest::AsyncWorkItem::Execute() \/ \/ \/ */ -HttpSysServer::HttpSysServer(const HttpSysConfig& Config) +HttpSysServer::HttpSysServer(const HttpSysConfig& InConfig) : m_Log(logging::Get("http")) , m_RequestLog(logging::Get("http_requests")) -, m_IsRequestLoggingEnabled(Config.IsRequestLoggingEnabled) -, m_IsAsyncResponseEnabled(Config.IsAsyncResponseEnabled) -, m_ThreadPool(Config.ThreadCount != 0 ? Config.ThreadCount : std::thread::hardware_concurrency()) -, m_InitialConfig(Config) +, m_IsRequestLoggingEnabled(InConfig.IsRequestLoggingEnabled) +, m_IsAsyncResponseEnabled(InConfig.IsAsyncResponseEnabled) +, m_InitialConfig(InConfig) { + // Initialize thread pool + + int MinThreadCount; + int MaxThreadCount; + + if (m_InitialConfig.ThreadCount == 0) + { + MinThreadCount = Max(8u, std::thread::hardware_concurrency()); + } + else + { + MinThreadCount = m_InitialConfig.ThreadCount; + } + + MaxThreadCount = MinThreadCount * 2; + + if (m_InitialConfig.IsDedicatedServer) + { + // In order to limit the potential impact of threads stuck + // in locks we allow the thread pool to be oversubscribed + // by a fair amount + + MaxThreadCount *= 2; + } + + m_IoThreadPool = std::make_unique<WinIoThreadPool>(MinThreadCount, MaxThreadCount); + + if (m_InitialConfig.AsyncWorkThreadCount == 0) + { + m_InitialConfig.AsyncWorkThreadCount = 16; + } + ULONG Result = HttpInitialize(HTTPAPI_VERSION_2, HTTP_INITIALIZE_SERVER, nullptr); if (Result != NO_ERROR) @@ -907,7 +942,11 @@ HttpSysServer::HttpSysServer(const HttpSysConfig& Config) m_IsHttpInitialized = true; m_IsOk = true; - ZEN_INFO("http.sys server started, using {} I/O threads and {} async worker threads", Config.ThreadCount, Config.AsyncWorkThreadCount); + ZEN_INFO("http.sys server started in {} mode, using {}-{} I/O threads and {} async worker threads", + m_InitialConfig.IsDedicatedServer ? "DEDICATED" : "NORMAL", + MinThreadCount, + MaxThreadCount, + m_InitialConfig.AsyncWorkThreadCount); } HttpSysServer::~HttpSysServer() @@ -1070,10 +1109,27 @@ HttpSysServer::InitializeServer(int BasePort) 0); } + // Tune the maximum number of pending requests in the http.sys request queue. By default + // the value is 1000 which is plenty for single user machines but for dedicated servers + // serving many users it makes sense to increase this to a higher number to help smooth + // out intermittent stalls like we might experience when GC is triggered + + if (m_InitialConfig.IsDedicatedServer) + { + ULONG QueueLength = 50000; + + Result = HttpSetRequestQueueProperty(m_RequestQueueHandle, HttpServerQueueLengthProperty, &QueueLength, sizeof QueueLength, 0, 0); + + if (Result != NO_ERROR) + { + ZEN_WARN("changing request queue length to {} failed: {}", QueueLength, Result); + } + } + // Create I/O completion port std::error_code ErrorCode; - m_ThreadPool.CreateIocp(m_RequestQueueHandle, HttpSysTransaction::IoCompletionCallback, /* Context */ this, /* out */ ErrorCode); + m_IoThreadPool->CreateIocp(m_RequestQueueHandle, HttpSysTransaction::IoCompletionCallback, /* Context */ this, /* out */ ErrorCode); if (ErrorCode) { @@ -1137,9 +1193,7 @@ HttpSysServer::WorkPool() if (!m_AsyncWorkPool) { - unsigned int WorkerThreadCount = m_InitialConfig.AsyncWorkThreadCount != 0 ? m_InitialConfig.AsyncWorkThreadCount : 16; - - m_AsyncWorkPool = new WorkerThreadPool(WorkerThreadCount, "http_async"); + m_AsyncWorkPool = new WorkerThreadPool(m_InitialConfig.AsyncWorkThreadCount, "http_async"); } } @@ -1305,7 +1359,7 @@ HttpSysTransaction::~HttpSysTransaction() PTP_IO HttpSysTransaction::Iocp() { - return m_HttpServer.m_ThreadPool.Iocp(); + return m_HttpServer.m_IoThreadPool->Iocp(); } HANDLE diff --git a/src/zenhttp/httpsys.h b/src/zenhttp/httpsys.h index 1553d56ef..6a6b16525 100644 --- a/src/zenhttp/httpsys.h +++ b/src/zenhttp/httpsys.h @@ -20,6 +20,7 @@ struct HttpSysConfig unsigned int AsyncWorkThreadCount = 0; bool IsAsyncResponseEnabled = true; bool IsRequestLoggingEnabled = false; + bool IsDedicatedServer = false; }; Ref<HttpServer> CreateHttpSysServer(HttpSysConfig Config); diff --git a/src/zenhttp/include/zenhttp/httpserver.h b/src/zenhttp/include/zenhttp/httpserver.h index c233075be..3cbe05dd6 100644 --- a/src/zenhttp/include/zenhttp/httpserver.h +++ b/src/zenhttp/include/zenhttp/httpserver.h @@ -184,7 +184,8 @@ public: struct HttpServerConfig { - std::string ServerClass; // Choice of HTTP server implementation + bool IsDedicatedServer = false; // Should be set to true for shared servers + std::string ServerClass; // Choice of HTTP server implementation unsigned int ThreadCount = 0; struct diff --git a/src/zenhttp/iothreadpool.cpp b/src/zenhttp/iothreadpool.cpp index 6087e69ec..da4b42e28 100644 --- a/src/zenhttp/iothreadpool.cpp +++ b/src/zenhttp/iothreadpool.cpp @@ -8,14 +8,19 @@ namespace zen { -WinIoThreadPool::WinIoThreadPool(int InThreadCount) +WinIoThreadPool::WinIoThreadPool(int InThreadCount, int InMaxThreadCount) { - // Thread pool setup + ZEN_ASSERT(InThreadCount); + + if (InMaxThreadCount < InThreadCount) + { + InMaxThreadCount = InThreadCount; + } m_ThreadPool = CreateThreadpool(NULL); SetThreadpoolThreadMinimum(m_ThreadPool, InThreadCount); - SetThreadpoolThreadMaximum(m_ThreadPool, InThreadCount * 2); + SetThreadpoolThreadMaximum(m_ThreadPool, InMaxThreadCount); InitializeThreadpoolEnvironment(&m_CallbackEnvironment); diff --git a/src/zenhttp/iothreadpool.h b/src/zenhttp/iothreadpool.h index 8333964c3..e75e95e58 100644 --- a/src/zenhttp/iothreadpool.h +++ b/src/zenhttp/iothreadpool.h @@ -11,16 +11,10 @@ namespace zen { -////////////////////////////////////////////////////////////////////////// -// -// Thread pool. Implemented in terms of Windows thread pool right now, will -// need a cross-platform implementation eventually -// - class WinIoThreadPool { public: - WinIoThreadPool(int InThreadCount); + WinIoThreadPool(int InThreadCount, int InMaxThreadCount); ~WinIoThreadPool(); void CreateIocp(HANDLE IoHandle, PTP_WIN32_IO_CALLBACK Callback, void* Context, std::error_code& ErrorCode); diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp index 85db7bade..cdd1fb031 100644 --- a/src/zenserver/config.cpp +++ b/src/zenserver/config.cpp @@ -1353,6 +1353,8 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions) { ServerOptions.AbsLogFile = ServerOptions.DataDir / "logs" / "zenserver.log"; } + + ServerOptions.HttpServerConfig.IsDedicatedServer = ServerOptions.IsDedicated; } } // namespace zen |