mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-07-11 22:03:01 -06:00
Core: finetuning allocator
git-svn-id: svn://ultimatepp.org/upp/trunk@13449 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
975da84113
commit
ca837944ba
10 changed files with 63 additions and 27 deletions
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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 ---------------------------
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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]()&]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue