From ca837944ba8e06352b6ecd386865c8de141978c4 Mon Sep 17 00:00:00 2001 From: cxl Date: Thu, 27 Jun 2019 13:17:50 +0000 Subject: [PATCH] Core: finetuning allocator git-svn-id: svn://ultimatepp.org/upp/trunk@13449 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/Core/Diag.h | 1 + uppsrc/Core/Format.cpp | 22 ++++++++++++++++------ uppsrc/Core/HeapImp.h | 4 ++-- uppsrc/Core/Log.cpp | 8 +++++++- uppsrc/Core/heap.cpp | 1 + uppsrc/Core/heaputil.cpp | 2 +- uppsrc/Core/hheap.cpp | 7 +++++-- uppsrc/Core/lheap.cpp | 12 +++++++----- uppsrc/Core/sheap.cpp | 19 ++++++++++++------- uppsrc/Core/src.tpp/Logging_en-us.tpp | 14 +++++++++++--- 10 files changed, 63 insertions(+), 27 deletions(-) diff --git a/uppsrc/Core/Diag.h b/uppsrc/Core/Diag.h index 60ef7755f..99c1e35ea 100644 --- a/uppsrc/Core/Diag.h +++ b/uppsrc/Core/Diag.h @@ -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; } diff --git a/uppsrc/Core/Format.cpp b/uppsrc/Core/Format.cpp index 6432e2bbf..4dd52d0d6 100644 --- a/uppsrc/Core/Format.cpp +++ b/uppsrc/Core/Format.cpp @@ -7,12 +7,22 @@ namespace Upp { String VFormat(const char *fmt, va_list ptr) { int limit = 2 * (int)strlen(fmt) + 1024; - Buffer 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 buffer(limit); + vsprintf(buffer, fmt, ptr); + va_end(ptr); + int len = (int)strlen(buffer); + ASSERT(len <= limit); + return String(buffer, len); + } } // Formatting routines --------------------------- diff --git a/uppsrc/Core/HeapImp.h b/uppsrc/Core/HeapImp.h index e37045a30..89b0d2efe 100644 --- a/uppsrc/Core/HeapImp.h +++ b/uppsrc/Core/HeapImp.h @@ -382,7 +382,7 @@ struct Heap : BlkHeap { 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 { 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); diff --git a/uppsrc/Core/Log.cpp b/uppsrc/Core/Log.cpp index 1e53878c8..f8172e35e 100644 --- a/uppsrc/Core/Log.cpp +++ b/uppsrc/Core/Log.cpp @@ -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) diff --git a/uppsrc/Core/heap.cpp b/uppsrc/Core/heap.cpp index f28ba26bd..a03f9f074 100644 --- a/uppsrc/Core/heap.cpp +++ b/uppsrc/Core/heap.cpp @@ -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]); } } diff --git a/uppsrc/Core/heaputil.cpp b/uppsrc/Core/heaputil.cpp index 28610a990..e28973a13 100644 --- a/uppsrc/Core/heaputil.cpp +++ b/uppsrc/Core/heaputil.cpp @@ -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; diff --git a/uppsrc/Core/hheap.cpp b/uppsrc/Core/hheap.cpp index 8e5ad03fb..bff1e9ece 100644 --- a/uppsrc/Core/hheap.cpp +++ b/uppsrc/Core/hheap.cpp @@ -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 diff --git a/uppsrc/Core/lheap.cpp b/uppsrc/Core/lheap.cpp index 8db73f41d..2b541ca3f 100644 --- a/uppsrc/Core/lheap.cpp +++ b/uppsrc/Core/lheap.cpp @@ -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; diff --git a/uppsrc/Core/sheap.cpp b/uppsrc/Core/sheap.cpp index c4afdc0ed..9f580bcdd 100644 --- a/uppsrc/Core/sheap.cpp +++ b/uppsrc/Core/sheap.cpp @@ -1,5 +1,7 @@ #include +#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"); diff --git a/uppsrc/Core/src.tpp/Logging_en-us.tpp b/uppsrc/Core/src.tpp/Logging_en-us.tpp index 4f564a621..f50399cde 100644 --- a/uppsrc/Core/src.tpp/Logging_en-us.tpp +++ b/uppsrc/Core/src.tpp/Logging_en-us.tpp @@ -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]()&]