Make Lazy Decomit asynchronous

On platforms that support low-memory notifications register callbacks
that perform lazy decommit. This allows idle processes to return memory
to the OS. Without incurring the cost of constantly committing and
decommitting memory.

Code review and CI changes

* Fixed test to use a template to make constexpr magic work
* Factored out basic notification mechanism so can be reused on other
platforms.
This commit is contained in:
Matthew Parkinson
2020-02-21 11:34:39 +00:00
parent 8723ae2443
commit 813367286e
4 changed files with 161 additions and 98 deletions

View File

@@ -1,6 +1,7 @@
#include <iostream>
#include <snmalloc.h>
#include <test/measuretime.h>
#include <test/opt.h>
#include <test/setup.h>
#include <unordered_set>
#include <vector>
@@ -37,29 +38,34 @@ public:
tail = tail->next;
}
void try_remove()
bool try_remove()
{
if (head->next == nullptr)
return;
return false;
Node* next = head->next;
ThreadAlloc::get()->dealloc(head);
head = next;
return true;
}
};
std::atomic<uint64_t> global_epoch = 0;
void advance(PalNotificationObject* unused)
{
UNUSED(unused);
global_epoch++;
}
PalNotificationObject update_epoch = {nullptr, &advance};
bool has_pressure()
{
static thread_local uint64_t epoch;
static thread_local uint64_t epoch = 0;
if constexpr (!pal_supports<LowMemoryNotification, GlobalVirtual>)
{
return false;
}
uint64_t current_epoch = default_memory_provider().low_memory_epoch();
bool result = epoch != current_epoch;
epoch = current_epoch;
bool result = epoch != global_epoch;
epoch = global_epoch;
return result;
}
@@ -79,7 +85,7 @@ void reach_pressure(Queue& allocations)
void reduce_pressure(Queue& allocations)
{
size_t size = 4096;
for (size_t n = 0; n < 1000; n++)
for (size_t n = 0; n < 10000; n++)
{
allocations.try_remove();
allocations.try_remove();
@@ -87,21 +93,51 @@ void reduce_pressure(Queue& allocations)
}
}
int main(int, char**)
/**
* Wrapper to handle Pals that don't have the method.
* Template parameter required to handle `if constexpr` always evaluating both
* sides.
**/
template<typename PAL>
void register_for_pal_notifications()
{
#ifndef NDEBUG
Queue allocations;
PAL::register_for_low_memory_callback(&update_epoch);
}
if constexpr (!pal_supports<LowMemoryNotification, GlobalVirtual>)
int main(int argc, char** argv)
{
opt::Opt opt(argc, argv);
if constexpr (pal_supports<LowMemoryNotification, GlobalVirtual>)
{
register_for_pal_notifications<GlobalVirtual>();
}
else
{
std::cout << "Pal does not support low-memory notification! Test not run"
<< std::endl;
return 0;
}
#ifdef NDEBUG
# if defined(WIN32) && !defined(SNMALLOC_VA_BITS_64)
std::cout << "32-bit windows not supported for this test." << std::endl;
# else
bool interactive = opt.has("--interactive");
Queue allocations;
std::cout
<< "Expected use:" << std::endl
<< " run first instances with --interactive. Wait for first to print "
<< std::endl
<< " 'No allocations left. Press any key to terminate'" << std::endl
<< "watch working set, and start second instance working set of first "
<< "should drop to almost zero," << std::endl
<< "and second should climb to physical ram." << std::endl
<< std::endl;
setup();
for (size_t i = 0; i < 10; i++)
@@ -111,6 +147,16 @@ int main(int, char**)
reduce_pressure(allocations);
}
// Deallocate everything
while (allocations.try_remove())
;
if (interactive)
{
std::cout << "No allocations left. Press any key to terminate" << std::endl;
getchar();
}
# endif
#else
std::cout << "Release test only." << std::endl;