// src/platform/memory.cpp - see include/strata/platform/memory.hpp. #include "strata/platform/memory.hpp" #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include #include #include #else #include #include #endif namespace strata::platform { #if defined(_WIN32) LockResult lock_resident(void* p, uint64_t bytes) { LockResult r; if (p == nullptr || bytes == 0) { r.note = "nothing to lock"; return r; } HANDLE self = GetCurrentProcess(); SIZE_T min_ws = 0, max_ws = 0; DWORD flags = 0; if (!GetProcessWorkingSetSizeEx(self, &min_ws, &max_ws, &flags)) { r.note = "GetProcessWorkingSetSizeEx failed (error " + std::to_string(GetLastError()) + ")"; return r; } // Locked pages count against the minimum working set, so it must grow by the region plus headroom for the // rest of the process. Soft limits: the maximum is not enforced, only the minimum is raised. const SIZE_T margin = (SIZE_T) 512 << 20; const SIZE_T new_min = min_ws + (SIZE_T) bytes + margin; const SIZE_T new_max = max_ws > new_min + margin ? max_ws : new_min + margin; if (!SetProcessWorkingSetSizeEx(self, new_min, new_max, QUOTA_LIMITS_HARDWS_MIN_DISABLE | QUOTA_LIMITS_HARDWS_MAX_DISABLE)) { r.note = "SetProcessWorkingSetSizeEx(" + std::to_string((unsigned long long) (new_min >> 20)) + " MiB) failed (error " + std::to_string(GetLastError()) + ")"; return r; } const uint64_t chunk = 1ull << 30; uint8_t* base = (uint8_t*) p; for (uint64_t off = 0; off < bytes; off += chunk) { const uint64_t n = bytes - off < chunk ? bytes - off : chunk; if (!VirtualLock(base + off, (SIZE_T) n)) { r.note = "VirtualLock stopped at " + std::to_string((unsigned long long) (off >> 20)) + " of " + std::to_string((unsigned long long) (bytes >> 20)) + " MiB (error " + std::to_string(GetLastError()) + ")"; r.ok = off > 0; return r; } r.locked_bytes = off + n; } r.ok = true; r.note = "locked " + std::to_string((unsigned long long) (bytes >> 20)) + " MiB via working-set minimum + VirtualLock"; return r; } void unlock_resident(void* p, uint64_t bytes) { if (p == nullptr || bytes == 0) return; const uint64_t chunk = 1ull << 30; for (uint64_t off = 0; off < bytes; off += chunk) VirtualUnlock((uint8_t*) p + off, (SIZE_T) (bytes - off < chunk ? bytes - off : chunk)); } bool gpu_shared_memory_budget(const void* luid, uint64_t& budget, uint64_t& usage, std::string& why) { budget = usage = 0; // dxgi.dll is loaded when asked, not linked: a start that never needs this keeps the imports it had HMODULE dxgi = LoadLibraryA("dxgi.dll"); if (dxgi == nullptr) { why = "dxgi.dll not found"; return false; } using CreateFactory = HRESULT(WINAPI*)(REFIID, void**); const auto create = (CreateFactory) (void*) GetProcAddress(dxgi, "CreateDXGIFactory1"); IDXGIFactory1* factory = nullptr; if (create == nullptr || FAILED(create(__uuidof(IDXGIFactory1), (void**) &factory)) || factory == nullptr) { why = "CreateDXGIFactory1 failed"; FreeLibrary(dxgi); return false; } bool ok = false; why = "no DXGI adapter has the CUDA device's LUID"; for (UINT i = 0; !ok; ++i) { IDXGIAdapter1* a = nullptr; if (factory->EnumAdapters1(i, &a) == DXGI_ERROR_NOT_FOUND || a == nullptr) break; DXGI_ADAPTER_DESC1 d{}; if (SUCCEEDED(a->GetDesc1(&d)) && std::memcmp(&d.AdapterLuid, luid, sizeof d.AdapterLuid) == 0) { IDXGIAdapter3* a3 = nullptr; DXGI_QUERY_VIDEO_MEMORY_INFO info{}; if (SUCCEEDED(a->QueryInterface(__uuidof(IDXGIAdapter3), (void**) &a3)) && a3 != nullptr && SUCCEEDED(a3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &info))) { budget = info.Budget; usage = info.CurrentUsage; ok = budget > 0; why = ok ? "" : "the adapter reports no shared-memory budget"; } else { why = "QueryVideoMemoryInfo failed"; } if (a3 != nullptr) a3->Release(); a->Release(); break; } a->Release(); } factory->Release(); FreeLibrary(dxgi); return ok; } uint64_t total_physical_memory() { MEMORYSTATUSEX ms{}; ms.dwLength = sizeof ms; return GlobalMemoryStatusEx(&ms) ? (uint64_t) ms.ullTotalPhys : 0; } #else LockResult lock_resident(void* p, uint64_t bytes) { LockResult r; if (p == nullptr || bytes == 0) { r.note = "nothing to lock"; return r; } if (mlock(p, bytes) != 0) { r.note = "mlock failed (raise ulimit -l)"; return r; } r.ok = true; r.locked_bytes = bytes; r.note = "mlock"; return r; } void unlock_resident(void* p, uint64_t bytes) { if (p != nullptr && bytes != 0) munlock(p, bytes); } bool gpu_shared_memory_budget(const void*, uint64_t& budget, uint64_t& usage, std::string& why) { budget = usage = 0; why = "DXGI is Windows-only"; return false; } uint64_t total_physical_memory() { const long pages = sysconf(_SC_PHYS_PAGES), page = sysconf(_SC_PAGE_SIZE); return pages > 0 && page > 0 ? (uint64_t) pages * (uint64_t) page : 0; } #endif } // namespace strata::platform