File size: 5,500 Bytes
bbb6388 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | // 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 <windows.h>
#include <dxgi1_4.h>
#include <cstring>
#else
#include <sys/mman.h>
#include <unistd.h>
#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
|