vk: Improve memory load balancing and reporting

- Track pool usage
- Take allocation percentages into account when making decisions on whether there is overloading or not
This commit is contained in:
kd-11 2021-07-14 00:35:57 +03:00 committed by kd-11
parent 342b25d00e
commit 53c9fb3e00
2 changed files with 100 additions and 27 deletions

View File

@ -5,8 +5,20 @@
namespace vk
{
std::unordered_map<uptr, vmm_allocation_t> g_vmm_allocations;
std::unordered_map<uptr, atomic_t<u64>> g_vmm_memory_usage;
struct vmm_memory_stats
{
std::unordered_map<uptr, vmm_allocation_t> allocations;
std::unordered_map<uptr, atomic_t<u64>> memory_usage;
std::unordered_map<vmm_allocation_pool, atomic_t<u64>> pool_usage;
void clear()
{
allocations.clear();
memory_usage.clear();
pool_usage.clear();
}
}
g_vmm_stats;
resource_manager g_resource_manager;
atomic_t<u64> g_event_ctr;
@ -57,20 +69,22 @@ namespace vk
void vmm_notify_memory_allocated(void* handle, u32 memory_type, u64 memory_size, vmm_allocation_pool pool)
{
auto key = reinterpret_cast<uptr>(handle);
const vmm_allocation_t info = { memory_size, memory_type };
const vmm_allocation_t info = { memory_size, memory_type, pool };
if (const auto ins = g_vmm_allocations.insert_or_assign(key, info);
if (const auto ins = g_vmm_stats.allocations.insert_or_assign(key, info);
!ins.second)
{
rsx_log.error("Duplicate vmm entry with memory handle 0x%llx", key);
}
auto& vmm_size = g_vmm_memory_usage[memory_type];
g_vmm_stats.pool_usage[pool] += memory_size;
auto& vmm_size = g_vmm_stats.memory_usage[memory_type];
vmm_size += memory_size;
if (vmm_size > s_vmm_warn_threshold_size && (vmm_size - memory_size) <= s_vmm_warn_threshold_size)
{
rsx_log.warning("Memory type 0x%x has allocated more than %.2fG. Currently allocated %.2fG",
rsx_log.warning("Memory type 0x%x has allocated more than %04.2fG. Currently allocated %04.2fG",
memory_type, size_in_GiB(s_vmm_warn_threshold_size), size_in_GiB(vmm_size));
}
}
@ -78,19 +92,90 @@ namespace vk
void vmm_notify_memory_freed(void* handle)
{
auto key = reinterpret_cast<uptr>(handle);
if (auto found = g_vmm_allocations.find(key);
found != g_vmm_allocations.end())
if (auto found = g_vmm_stats.allocations.find(key);
found != g_vmm_stats.allocations.end())
{
const auto& info = found->second;
g_vmm_memory_usage[info.type_index] -= info.size;
g_vmm_allocations.erase(found);
g_vmm_stats.memory_usage[info.type_index] -= info.size;
g_vmm_stats.pool_usage[info.pool] -= info.size;
g_vmm_stats.allocations.erase(found);
}
}
void vmm_reset()
{
g_vmm_memory_usage.clear();
g_vmm_allocations.clear();
g_vmm_stats.clear();
g_event_ctr = 0;
g_last_completed_event = 0;
}
u64 vmm_get_application_memory_usage(u32 memory_type)
{
return g_vmm_stats.memory_usage[memory_type];
}
u64 vmm_get_application_pool_usage(vmm_allocation_pool pool)
{
return g_vmm_stats.pool_usage[pool];
}
rsx::problem_severity vmm_determine_memory_load_severity()
{
const auto vmm_load = get_current_mem_allocator()->get_memory_usage();
rsx::problem_severity load_severity = rsx::problem_severity::low;
// Fragmentation tuning
if (vmm_load < 50.f)
{
get_current_mem_allocator()->set_fastest_allocation_flags();
}
else if (vmm_load > 75.f)
{
// Avoid fragmentation if we can
get_current_mem_allocator()->set_safest_allocation_flags();
if (vmm_load > 95.f)
{
// Drivers will often crash long before returning OUT_OF_DEVICE_MEMORY errors.
load_severity = rsx::problem_severity::fatal;
}
else if (vmm_load > 90.f)
{
load_severity = rsx::problem_severity::severe;
}
else
{
load_severity = rsx::problem_severity::moderate;
}
// Query actual usage for comparison. Maybe we just have really fragmented memory...
const auto mem_info = get_current_renderer()->get_memory_mapping();
const auto local_memory_usage = vmm_get_application_memory_usage(mem_info.device_local);
constexpr u64 _1GB = (1024 * 0x100000);
if (local_memory_usage < (mem_info.device_local_total_bytes / 2) || // Less than 50% VRAM usage OR
(mem_info.device_local_total_bytes - local_memory_usage) > _1GB) // More than 1GiB of unallocated memory
{
// Lower severity to avoid slowing performance too much
load_severity = rsx::problem_severity::low;
}
else if ((mem_info.device_local_total_bytes - local_memory_usage) > 512 * 0x100000)
{
// At least 512MB left, do not overreact
load_severity = rsx::problem_severity::moderate;
}
if (load_severity >= rsx::problem_severity::moderate)
{
// NOTE: For some reason fmt::format with a sized float followed by percentage sign causes random crashing.
// This is a bug unrelated to this, but explains why we're going with integral percentages here.
const auto application_memory_load = (local_memory_usage * 100) / mem_info.device_local_total_bytes;
rsx_log.warning("Actual device memory used by internal allocations is %lluM (%llu%%)", local_memory_usage / 0x100000, application_memory_load);
rsx_log.warning("Video memory usage is at %d%%. Will attempt to reclaim some resources.", static_cast<int>(vmm_load));
}
}
return load_severity;
}
bool vmm_handle_memory_pressure(rsx::problem_severity severity)
@ -105,21 +190,8 @@ namespace vk
void vmm_check_memory_usage()
{
const auto vmm_load = get_current_mem_allocator()->get_memory_usage();
rsx::problem_severity load_severity = rsx::problem_severity::low;
if (vmm_load > 90.f)
{
rsx_log.warning("Video memory usage exceeding 90%. Will attempt to reclaim resources.");
load_severity = rsx::problem_severity::severe;
}
else if (vmm_load > 75.f)
{
rsx_log.notice("Video memory usage exceeding 75%. Will attempt to reclaim resources.");
load_severity = rsx::problem_severity::moderate;
}
if (load_severity >= rsx::problem_severity::moderate)
if (const auto load_severity = vmm_determine_memory_load_severity();
load_severity >= rsx::problem_severity::moderate)
{
vmm_handle_memory_pressure(load_severity);
}

View File

@ -183,6 +183,7 @@ namespace vk
{
u64 size;
u32 type_index;
vmm_allocation_pool pool;
};
resource_manager* get_resource_manager();