Core: finetuning allocator

git-svn-id: svn://ultimatepp.org/upp/trunk@13449 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2019-06-27 13:17:50 +00:00
parent 975da84113
commit ca837944ba
10 changed files with 63 additions and 27 deletions

View file

@ -10,6 +10,7 @@ const char LOG_END = '\x1f';
enum LogOptions {
LOG_FILE = 1, LOG_COUT = 2, LOG_CERR = 4, LOG_DBG = 8, LOG_SYS = 16, LOG_ELAPSED = 128,
LOG_TIMESTAMP = 256, LOG_TIMESTAMP_UTC = 512, LOG_APPEND = 1024, LOG_ROTATE_GZIP = 2048,
LOG_COUTW = 4096, LOG_CERRW = 8192
};
inline int LOG_ROTATE(int x) { return x << 24; }

View file

@ -7,12 +7,22 @@ namespace Upp {
String VFormat(const char *fmt, va_list ptr) {
int limit = 2 * (int)strlen(fmt) + 1024;
Buffer<char> buffer(limit);
vsprintf(buffer, fmt, ptr);
va_end(ptr);
int len = (int)strlen(buffer);
ASSERT(len <= limit);
return String(buffer, len);
if(limit < 1500) {
char buffer[1500];
vsprintf(buffer, fmt, ptr);
va_end(ptr);
int len = (int)strlen(buffer);
ASSERT(len <= limit);
return String(buffer, len);
}
else {
Buffer<char> buffer(limit);
vsprintf(buffer, fmt, ptr);
va_end(ptr);
int len = (int)strlen(buffer);
ASSERT(len <= limit);
return String(buffer, len);
}
}
// Formatting routines ---------------------------

View file

@ -382,7 +382,7 @@ struct Heap : BlkHeap<HugeHeapDetail, 4096> {
static Heap aux; // Single global auxiliary heap to store orphans and global list of free pages
static size_t huge_4KB_count; // total number of 4KB pages in small/large/huge blocks
static size_t free_4KB; // empty 4KB pages
static int free_4KB; // empty 4KB pages
static size_t big_size; // blocks >~64KB
static size_t big_count;
static size_t sys_size; // blocks allocated directly from system (included in big too)
@ -390,7 +390,7 @@ struct Heap : BlkHeap<HugeHeapDetail, 4096> {
static size_t huge_chunks; // 32MB master pages
static size_t huge_4KB_count_max; // peak huge memory allocated
static HugePage *free_huge_pages; // list of records of freed hpages (to be reused)
static int free_hpages;
static int free_hpages; // empty huge pages (in reserve)
#ifdef HEAPDBG
static void DbgFreeFillK(void *ptr, int k);

View file

@ -209,8 +209,14 @@ void LogOut::Line(const char *s, int len, int depth)
int count = (int)(p - h);
if(count == 0) return;
if(options & LOG_COUT)
Cout().Put(h, count);
for(const char *s = beg; *s; s++)
putchar(*s);
if(options & LOG_CERR)
for(const char *s = beg; *s; s++)
putc(*s, stderr);
if(options & LOG_COUTW)
Cout().Put(h, count);
if(options & LOG_CERRW)
Cerr().Put(h, count);
#ifdef PLATFORM_WIN32
if(options & LOG_FILE)

View file

@ -123,6 +123,7 @@ void Heap::Shutdown()
empty[i]->heap = &aux;
empty[i]->next = aux.empty[i];
aux.empty[i] = empty[i];
free_4KB++;
LLOG("Orphan empty " << (void *)empty[i]);
}
}

View file

@ -18,7 +18,7 @@ void OutOfMemoryPanic(size_t size)
}
size_t Heap::huge_4KB_count;
size_t Heap::free_4KB;
int Heap::free_4KB;
size_t Heap::big_size;
size_t Heap::big_count;
size_t Heap::sys_size;

View file

@ -43,7 +43,7 @@ EXITBLOCK {
}
#endif
int sKBLimit = INT_MAX;
size_t sKBLimit = INT_MAX;
void MemoryLimitKb(int kb)
{
@ -66,6 +66,7 @@ int Heap::free_hpages;
void *Heap::HugeAlloc(size_t count) // count in 4kb pages
{
LTIMING("HugeAlloc");
ASSERT(count);
#ifdef LSTAT
@ -78,7 +79,7 @@ void *Heap::HugeAlloc(size_t count) // count in 4kb pages
auto MaxMem = [&] {
if(huge_4KB_count > huge_4KB_count_max) {
huge_4KB_count_max = huge_4KB_count;
if(MemoryUsedKb() > sKBLimit)
if(4 * (Heap::huge_4KB_count - Heap::free_4KB) > sKBLimit)
Panic("MemoryLimitKb breached!");
if(sPeak)
Make(*sPeak);
@ -148,6 +149,7 @@ void *Heap::HugeAlloc(size_t count) // count in 4kb pages
int Heap::HugeFree(void *ptr)
{
LTIMING("HugeFree");
BlkHeader *h = (BlkHeader *)ptr;
if(h->size == 0) {
LTIMING("Sys Free");
@ -165,6 +167,7 @@ int Heap::HugeFree(void *ptr)
int sz = h->GetSize();
if(h->IsFirst() && h->IsLast())
if(free_hpages >= max_free_hpages) { // we have enough pages in the reserve, return to the system
LTIMING("Free Huge Page");
h->UnlinkFree();
HugePage *p = NULL;
while(huge_pages) { // remove the page from the set of huge pages

View file

@ -1,14 +1,13 @@
#include "Core.h"
#include "Core.h"
#define LTIMING(x) // RTIMING(x)
#define LTIMING(x) // RTIMING(x)
#define LHITCOUNT(x) // RHITCOUNT(x)
#define LLOG(x) // LOG((void *)this << ' ' << x)
namespace Upp {
#ifdef UPP_HEAP
#define LLOG(x) // LOG((void *)this << ' ' << x)
#include "HeapImp.h"
int Heap::lclass[] = { 0, 4, 5, 6, 7, 8, 9, 11, 13, 15, 18, 22, 27, 33, 40, 49, 60, 73, 89, 109, 134, 164, 201, 225, 255 };
@ -43,6 +42,7 @@ void Heap::LInit()
void *Heap::TryLAlloc(int i0, word wcount)
{
LTIMING("TryLAlloc");
for(int i = i0; i < __countof(lheap.freelist); i++) {
LBlkHeader *l = lheap.freelist[i];
LBlkHeader *h = l->next;
@ -180,6 +180,7 @@ void Heap::LFree(void *ptr)
bool Heap::TryRealloc(void *ptr, size_t& newsize)
{
LTIMING("TryRealloc");
ASSERT(ptr);
#ifdef _DEBUG
@ -196,13 +197,14 @@ bool Heap::TryRealloc(void *ptr, size_t& newsize)
size_t dummy;
if(wcount == h->GetSize() || lheap.TryRealloc(h, wcount, dummy)) {
newsize = ((int)wcount * LUNIT) - sizeof(BlkPrefix);
LHITCOUNT("Large realloc true");
return true;
}
}
Mutex::Lock __(mutex);
if(h->heap == NULL) { // this is big block
LTIMING("Big Free");
LTIMING("Big realloc");
DLink *d = (DLink *)h - 1;

View file

@ -1,5 +1,7 @@
#include <Core/Core.h>
#define LTIMING(x) // RTIMING(x)
namespace Upp {
#ifdef UPP_HEAP
@ -57,19 +59,21 @@ Heap::Page *Heap::WorkPage(int k) // get a new workpage with empty blocks
page->Unlink();
LLOG("AllocK - adopting aux page " << k << " page: " << (void *)page << ", free " << (void *)page->freelist);
}
if(!page && aux.empty[k]) { // Try hot empty page of the same klass
if(!page && aux.empty[k]) { // Try free page of the same klass (no need to format it)
page = aux.empty[k];
aux.empty[k] = page->next;
free_4KB--;
ASSERT(free_4KB < max_free_spages);
LLOG("AllocK - empty aux page available of the same format " << k << " page: " << (void *)page << ", free " << (void *)page->freelist);
}
if(!page)
for(int i = 0; i < NKLASS; i++) // Finally try to to find hot page of different klass
for(int i = 0; i < NKLASS; i++) // Finally try to find free page of different klass
if(aux.empty[i]) {
page = aux.empty[i];
aux.empty[i] = page->next;
free_4KB--;
page->Format(k);
ASSERT(free_4KB < max_free_spages);
LLOG("AllocK - empty aux page available for reformatting " << k << " page: " << (void *)page << ", free " << (void *)page->freelist);
break;
}
@ -127,6 +131,7 @@ void *Heap::Allok(int k)
force_inline
void *Heap::AllocSz(size_t& sz)
{
LTIMING("Alloc");
LLOG("Alloc " << asString(sz));
Stat(sz);
if(sz <= 384) {
@ -177,15 +182,16 @@ void Heap::FreeK(void *ptr, Page *page, int k)
LLOG("Free page is empty " << (void *)page);
page->Unlink();
if(this == &aux) {
LLOG("...is aux");
LLOG("...is aux " << asString(free_4KB));
page->next = empty[k];
empty[k] = page;
free_4KB++;
}
else {
if(empty[k]) { // Keep one hot empty page per klass in thread, put rest to 'aux' global storage
LLOG("Global free " << k << " " << (void *)empty[k]);
Mutex::Lock __(mutex);
if(free_4KB < max_free_spages) {
if(free_4KB < max_free_spages) { // only keep max_free_spages, release if more
empty[k]->heap = &aux;
empty[k]->next = aux.empty[k];
aux.empty[k] = empty[k];
@ -204,7 +210,7 @@ void Heap::Free(void *ptr, Page *page, int k)
{
LLOG("Small free page: " << (void *)page << ", k: " << k << ", ksz: " << Ksz(k));
ASSERT((4096 - ((uintptr_t)ptr & (uintptr_t)4095)) % Ksz(k) == 0);
if(page->heap != this) { // freeing page allocated in different thread
if(page->heap != this) { // freeing block allocated in different thread
RemoteFree(ptr, Ksz(k)); // add to originating heap's list of free pages to be properly freed later
return;
}
@ -266,6 +272,7 @@ bool Heap::FreeSmallEmpty(int size4KB, int count)
Page *q = aux.empty[i];
aux.empty[i] = q->next;
free_4KB--;
ASSERT(free_4KB < max_free_spages);
if(aux.HugeFree(q) >= size4KB || --count <= 0) // HugeFree is really static, aux needed just to compile
return true;
released = true;
@ -358,8 +365,6 @@ size_t GetMemoryBlockSize_(void *ptr)
#else
#define LTIMING(x) // RTIMING(x)
void *MemoryAlloc(size_t sz)
{
LTIMING("MemoryAlloc");

View file

@ -44,9 +44,9 @@ bit flags:&]
:: [s0; Output log to file (this is default). The default path of file
is ConfigFile(`"[/ program`_name].log`").]
:: [s0; LOG`_COUT ]
:: [s0; Output log to standard output.]
:: [s0; Output log to standard output, using plain putchar.]
:: [s0; LOG`_CERR ]
:: [s0; Output log to error output.]
:: [s0; Output log to error output, using plain putc.]
:: [s0; LOG`_DBG ]
:: [s0; Output log to debugger (Win32 specific).]
:: [s0; LOG`_SYS ]
@ -68,7 +68,15 @@ of replacing it.]
(renamed with extension `'.1`', `'.2`' etc...).]
:: [s0; LOG`_ROTATE`_GZIP]
:: [s0; Older preserved log files are compressed using gzip (except
the most recent log `'.1`'.]}}&]
the most recent log `'.1`'.]
:: [s0; LOG`_COUTW]
:: [s0; Output log to standard output, using Cout. This provides eventual
conversion of UTF8 characters, at the price of using heap (so
cannot be used to e.g. LOG inside heap routines).]
:: [s0; LOG`_CERRW]
:: [s0; Output log to standard output, using Cerr. This provides eventual
conversion of UTF8 characters, at the price of using heap (so
cannot be used to e.g. LOG inside heap routines).]}}&]
[s3; &]
[s4;%- &]
[s5;:StdLog`(`):%- [_^Stream^ Stream][@(0.0.255) `&]_[* StdLog]()&]