mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
move memorypool
This commit is contained in:
parent
a048bf385d
commit
5e5548809a
@ -1,9 +1,6 @@
|
|||||||
-Wall
|
-Wall
|
||||||
-W*
|
-W*
|
||||||
|
|
||||||
-stdlib=libc++
|
|
||||||
-std=c++17
|
|
||||||
|
|
||||||
-Iinclude/
|
-Iinclude/
|
||||||
-I3rd/cjson/include/
|
-I3rd/cjson/include/
|
||||||
-I3rd/lua_bridge/include/
|
-I3rd/lua_bridge/include/
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
||||||
|
#define PK_VERSION "2.0.0"
|
||||||
|
#define PK_VERSION_MAJOR 2
|
||||||
|
#define PK_VERSION_MINOR 0
|
||||||
|
#define PK_VERSION_PATCH 0
|
||||||
|
|
||||||
/*************** feature settings ***************/
|
/*************** feature settings ***************/
|
||||||
|
|
||||||
// Whether to compile os-related modules or not
|
// Whether to compile os-related modules or not
|
||||||
@ -58,3 +63,8 @@
|
|||||||
#define PK_PLATFORM_SEP '/'
|
#define PK_PLATFORM_SEP '/'
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if PK_ENABLE_THREAD
|
||||||
|
#define PK_THREAD_LOCAL thread_local
|
||||||
|
#else
|
||||||
|
#define PK_THREAD_LOCAL static
|
||||||
|
#endif
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#if PK_ENABLE_THREAD
|
|
||||||
#define PK_THREAD_LOCAL thread_local
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
struct GIL {
|
|
||||||
inline static std::mutex _mutex;
|
|
||||||
|
|
||||||
explicit GIL() { _mutex.lock(); }
|
|
||||||
|
|
||||||
~GIL() { _mutex.unlock(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#define PK_GLOBAL_SCOPE_LOCK() GIL _lock;
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define PK_THREAD_LOCAL static
|
|
||||||
#define PK_GLOBAL_SCOPE_LOCK()
|
|
||||||
#endif
|
|
31
include/pocketpy/common/memorypool.h
Normal file
31
include/pocketpy/common/memorypool.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define kPoolExprBlockSize 128
|
||||||
|
#define kPoolFrameBlockSize 80
|
||||||
|
#define kPoolObjectBlockSize 80
|
||||||
|
|
||||||
|
#define kPoolObjectArenaSize (256*1024)
|
||||||
|
#define kPoolObjectMaxBlocks (kPoolObjectArenaSize / kPoolObjectBlockSize)
|
||||||
|
|
||||||
|
void Pools_initialize();
|
||||||
|
void Pools_finalize();
|
||||||
|
|
||||||
|
void* PoolExpr_alloc();
|
||||||
|
void PoolExpr_dealloc(void*);
|
||||||
|
void* PoolFrame_alloc();
|
||||||
|
void PoolFrame_dealloc(void*);
|
||||||
|
|
||||||
|
void* PoolObject_alloc();
|
||||||
|
void PoolObject_dealloc(void* p);
|
||||||
|
void PoolObject_shrink_to_fit();
|
||||||
|
|
||||||
|
void Pools_debug_info(char* buffer, int size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pocketpy/common/gil.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cassert>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace pkpy {
|
|
||||||
|
|
||||||
const inline int kPoolExprBlockSize = 128;
|
|
||||||
const inline int kPoolFrameBlockSize = 80;
|
|
||||||
const inline int kPoolObjectBlockSize = 80;
|
|
||||||
|
|
||||||
void* PoolExpr_alloc() noexcept;
|
|
||||||
void PoolExpr_dealloc(void*) noexcept;
|
|
||||||
void* PoolFrame_alloc() noexcept;
|
|
||||||
void PoolFrame_dealloc(void*) noexcept;
|
|
||||||
|
|
||||||
void* PoolObject_alloc() noexcept;
|
|
||||||
void PoolObject_dealloc(void* p) noexcept;
|
|
||||||
void PoolObject_shrink_to_fit() noexcept;
|
|
||||||
|
|
||||||
void Pools_debug_info(char* buffer, int size) noexcept;
|
|
||||||
|
|
||||||
}; // namespace pkpy
|
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "pocketpy/common/sstream.h"
|
#include "pocketpy/common/sstream.h"
|
||||||
#include "pocketpy/common/utils.h"
|
#include "pocketpy/common/utils.h"
|
||||||
#include "pocketpy/common/memorypool.hpp"
|
#include "pocketpy/common/memorypool.h"
|
||||||
#include "pocketpy/common/vector.h"
|
#include "pocketpy/common/vector.h"
|
||||||
#include "pocketpy/common/vector.hpp"
|
#include "pocketpy/common/vector.hpp"
|
||||||
#include "pocketpy/common/str.h"
|
#include "pocketpy/common/str.h"
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
// clang-format off
|
|
||||||
|
|
||||||
#define PK_VERSION "2.0.0"
|
|
||||||
#define PK_VERSION_MAJOR 2
|
|
||||||
#define PK_VERSION_MINOR 0
|
|
||||||
#define PK_VERSION_PATCH 0
|
|
336
src/common/memorypool.c
Normal file
336
src/common/memorypool.c
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
#include "pocketpy/common/memorypool.h"
|
||||||
|
#include "pocketpy/common/config.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct LinkedListNode {
|
||||||
|
struct LinkedListNode* prev;
|
||||||
|
struct LinkedListNode* next;
|
||||||
|
} LinkedListNode;
|
||||||
|
|
||||||
|
typedef struct LinkedList {
|
||||||
|
int count;
|
||||||
|
LinkedListNode head;
|
||||||
|
LinkedListNode tail;
|
||||||
|
} LinkedList;
|
||||||
|
|
||||||
|
static void LinkedList__ctor(LinkedList* self) {
|
||||||
|
self->count = 0;
|
||||||
|
self->head.prev = NULL;
|
||||||
|
self->head.next = &self->tail;
|
||||||
|
self->tail.prev = &self->head;
|
||||||
|
self->tail.next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LinkedList__push_back(LinkedList* self, LinkedListNode* node) {
|
||||||
|
node->prev = self->tail.prev;
|
||||||
|
node->next = &self->tail;
|
||||||
|
self->tail.prev->next = node;
|
||||||
|
self->tail.prev = node;
|
||||||
|
self->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LinkedList__push_front(LinkedList* self, LinkedListNode* node) {
|
||||||
|
node->prev = &self->head;
|
||||||
|
node->next = self->head.next;
|
||||||
|
self->head.next->prev = node;
|
||||||
|
self->head.next = node;
|
||||||
|
self->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LinkedList__pop_back(LinkedList* self) {
|
||||||
|
assert(self->count > 0);
|
||||||
|
self->tail.prev->prev->next = &self->tail;
|
||||||
|
self->tail.prev = self->tail.prev->prev;
|
||||||
|
self->count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LinkedListNode* LinkedList__back(LinkedList* self) {
|
||||||
|
assert(self->count > 0);
|
||||||
|
return self->tail.prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LinkedList__erase(LinkedList* self, LinkedListNode* node) {
|
||||||
|
node->prev->next = node->next;
|
||||||
|
node->next->prev = node->prev;
|
||||||
|
self->count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LinkedList__apply(self, __STATEMENTS__) \
|
||||||
|
do { \
|
||||||
|
LinkedListNode* node = (self)->head.next; \
|
||||||
|
while(node != &(self)->tail) { \
|
||||||
|
LinkedListNode* next = node->next; \
|
||||||
|
__STATEMENTS__ \
|
||||||
|
node = next; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
typedef struct MemoryPoolBlock{
|
||||||
|
void* arena;
|
||||||
|
char data[kPoolObjectBlockSize];
|
||||||
|
} MemoryPoolBlock;
|
||||||
|
|
||||||
|
typedef struct MemoryPoolArena{
|
||||||
|
/* LinkedListNode */
|
||||||
|
LinkedListNode* prev;
|
||||||
|
LinkedListNode* next;
|
||||||
|
/* Arena */
|
||||||
|
MemoryPoolBlock _blocks[kPoolObjectMaxBlocks];
|
||||||
|
MemoryPoolBlock* _free_list[kPoolObjectMaxBlocks];
|
||||||
|
int _free_list_size;
|
||||||
|
} MemoryPoolArena;
|
||||||
|
|
||||||
|
typedef struct MemoryPool{
|
||||||
|
LinkedList _arenas;
|
||||||
|
LinkedList _empty_arenas;
|
||||||
|
} MemoryPool;
|
||||||
|
|
||||||
|
static void MemoryPoolArena__ctor(MemoryPoolArena* self) {
|
||||||
|
self->prev = NULL;
|
||||||
|
self->next = NULL;
|
||||||
|
self->_free_list_size = kPoolObjectMaxBlocks;
|
||||||
|
for(int i = 0; i < kPoolObjectMaxBlocks; i++) {
|
||||||
|
self->_blocks[i].arena = self;
|
||||||
|
self->_free_list[i] = &self->_blocks[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool MemoryPoolArena__empty(MemoryPoolArena* self) {
|
||||||
|
return self->_free_list_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool MemoryPoolArena__full(MemoryPoolArena* self) {
|
||||||
|
return self->_free_list_size == kPoolObjectMaxBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int MemoryPoolArena__total_bytes(MemoryPoolArena* self) {
|
||||||
|
return kPoolObjectBlockSize * kPoolObjectMaxBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int MemoryPoolArena__used_bytes(MemoryPoolArena* self) {
|
||||||
|
return kPoolObjectBlockSize * (kPoolObjectMaxBlocks - self->_free_list_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemoryPoolBlock* MemoryPoolArena__alloc(MemoryPoolArena* self) {
|
||||||
|
assert(!MemoryPoolArena__empty(self));
|
||||||
|
self->_free_list_size--;
|
||||||
|
return self->_free_list[self->_free_list_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MemoryPoolArena__dealloc(MemoryPoolArena* self, MemoryPoolBlock* block) {
|
||||||
|
assert(!MemoryPoolArena__full(self));
|
||||||
|
self->_free_list[self->_free_list_size] = block;
|
||||||
|
self->_free_list_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MemoryPool__ctor(MemoryPool* self) {
|
||||||
|
LinkedList__ctor(&self->_arenas);
|
||||||
|
LinkedList__ctor(&self->_empty_arenas);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* MemoryPool__alloc(MemoryPool* self) {
|
||||||
|
MemoryPoolArena* arena;
|
||||||
|
if(self->_arenas.count == 0){
|
||||||
|
arena = malloc(sizeof(MemoryPoolArena));
|
||||||
|
MemoryPoolArena__ctor(arena);
|
||||||
|
LinkedList__push_back(&self->_arenas, (LinkedListNode*)arena);
|
||||||
|
} else {
|
||||||
|
arena = (MemoryPoolArena*)LinkedList__back(&self->_arenas);
|
||||||
|
}
|
||||||
|
void* p = MemoryPoolArena__alloc(arena)->data;
|
||||||
|
if(MemoryPoolArena__empty(arena)) {
|
||||||
|
LinkedList__pop_back(&self->_arenas);
|
||||||
|
LinkedList__push_back(&self->_empty_arenas, (LinkedListNode*)arena);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MemoryPool__dealloc(MemoryPool* self, void* p) {
|
||||||
|
assert(p != NULL);
|
||||||
|
MemoryPoolBlock* block = (MemoryPoolBlock*)((char*)p - sizeof(void*));
|
||||||
|
assert(block->arena != NULL);
|
||||||
|
MemoryPoolArena* arena = (MemoryPoolArena*)block->arena;
|
||||||
|
if(MemoryPoolArena__empty(arena)) {
|
||||||
|
LinkedList__erase(&self->_empty_arenas, (LinkedListNode*)arena);
|
||||||
|
LinkedList__push_front(&self->_arenas, (LinkedListNode*)arena);
|
||||||
|
}
|
||||||
|
MemoryPoolArena__dealloc(arena, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MemoryPool__shrink_to_fit(MemoryPool* self) {
|
||||||
|
const int MIN_ARENA_COUNT = PK_GC_MIN_THRESHOLD * 100 / (kPoolObjectArenaSize);
|
||||||
|
if(self->_arenas.count < MIN_ARENA_COUNT) return;
|
||||||
|
LinkedList__apply(&self->_arenas,
|
||||||
|
MemoryPoolArena* arena = (MemoryPoolArena*)node;
|
||||||
|
if(MemoryPoolArena__full(arena)) {
|
||||||
|
LinkedList__erase(&self->_arenas, node);
|
||||||
|
free(arena);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void MemoryPool__dtor(MemoryPool* self) {
|
||||||
|
LinkedList__apply(&self->_arenas, free(node););
|
||||||
|
LinkedList__apply(&self->_empty_arenas, free(node););
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct FixedMemoryPool {
|
||||||
|
int BlockSize;
|
||||||
|
int BlockCount;
|
||||||
|
|
||||||
|
char* data;
|
||||||
|
char* data_end;
|
||||||
|
int exceeded_bytes;
|
||||||
|
|
||||||
|
char** _free_list;
|
||||||
|
char** _free_list_end;
|
||||||
|
} FixedMemoryPool;
|
||||||
|
|
||||||
|
static void FixedMemoryPool__ctor(FixedMemoryPool* self, int BlockSize, int BlockCount) {
|
||||||
|
self->BlockSize = BlockSize;
|
||||||
|
self->BlockCount = BlockCount;
|
||||||
|
self->exceeded_bytes = 0;
|
||||||
|
self->data = malloc(BlockSize * BlockCount);
|
||||||
|
self->data_end = self->data + BlockSize * BlockCount;
|
||||||
|
self->_free_list = malloc(sizeof(void*) * BlockCount);
|
||||||
|
self->_free_list_end = self->_free_list;
|
||||||
|
for(int i = 0; i < BlockCount; i++) {
|
||||||
|
self->_free_list[i] = self->data + i * BlockSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FixedMemoryPool__dtor(FixedMemoryPool* self) {
|
||||||
|
free(self->_free_list);
|
||||||
|
free(self->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* FixedMemoryPool__alloc(FixedMemoryPool* self) {
|
||||||
|
if(self->_free_list_end != self->_free_list) {
|
||||||
|
self->_free_list_end--;
|
||||||
|
return *self->_free_list_end;
|
||||||
|
} else {
|
||||||
|
self->exceeded_bytes += self->BlockSize;
|
||||||
|
return malloc(self->BlockSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FixedMemoryPool__dealloc(FixedMemoryPool* self, void* p) {
|
||||||
|
bool is_valid = (char*)p >= self->data && (char*)p < self->data_end;
|
||||||
|
if(is_valid) {
|
||||||
|
*self->_free_list_end = p;
|
||||||
|
self->_free_list_end++;
|
||||||
|
} else {
|
||||||
|
self->exceeded_bytes -= self->BlockSize;
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int FixedMemoryPool__used_bytes(FixedMemoryPool* self) {
|
||||||
|
return (self->_free_list_end - self->_free_list) * self->BlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int FixedMemoryPool__total_bytes(FixedMemoryPool* self) {
|
||||||
|
return self->BlockCount * self->BlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
PK_THREAD_LOCAL FixedMemoryPool PoolExpr;
|
||||||
|
PK_THREAD_LOCAL FixedMemoryPool PoolFrame;
|
||||||
|
PK_THREAD_LOCAL MemoryPool PoolObject;
|
||||||
|
PK_THREAD_LOCAL bool initialized = false;
|
||||||
|
|
||||||
|
void Pools_initialize(){
|
||||||
|
if(initialized) return;
|
||||||
|
FixedMemoryPool__ctor(&PoolExpr, kPoolExprBlockSize, 64);
|
||||||
|
FixedMemoryPool__ctor(&PoolFrame, kPoolFrameBlockSize, 128);
|
||||||
|
MemoryPool__ctor(&PoolObject);
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pools_finalize(){
|
||||||
|
if(!initialized) return;
|
||||||
|
FixedMemoryPool__dtor(&PoolExpr);
|
||||||
|
FixedMemoryPool__dtor(&PoolFrame);
|
||||||
|
MemoryPool__dtor(&PoolObject);
|
||||||
|
initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* PoolExpr_alloc() {
|
||||||
|
assert(initialized);
|
||||||
|
return FixedMemoryPool__alloc(&PoolExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoolExpr_dealloc(void* p) {
|
||||||
|
assert(initialized);
|
||||||
|
FixedMemoryPool__dealloc(&PoolExpr, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* PoolFrame_alloc() {
|
||||||
|
assert(initialized);
|
||||||
|
return FixedMemoryPool__alloc(&PoolFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoolFrame_dealloc(void* p) {
|
||||||
|
assert(initialized);
|
||||||
|
FixedMemoryPool__dealloc(&PoolFrame, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* PoolObject_alloc() {
|
||||||
|
assert(initialized);
|
||||||
|
return MemoryPool__alloc(&PoolObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoolObject_dealloc(void* p) {
|
||||||
|
assert(initialized);
|
||||||
|
MemoryPool__dealloc(&PoolObject, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoolObject_shrink_to_fit() {
|
||||||
|
assert(initialized);
|
||||||
|
MemoryPool__shrink_to_fit(&PoolObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pools_debug_info(char* buffer, int size) {
|
||||||
|
double BYTES_PER_MB = 1024.0f * 1024.0f;
|
||||||
|
double BYTES_PER_KB = 1024.0f;
|
||||||
|
int n = 0;
|
||||||
|
n = snprintf(
|
||||||
|
buffer, size, "PoolExpr: %.2f KB (used) / %.2f KB (total) - %.2f KB (exceeded)\n",
|
||||||
|
FixedMemoryPool__used_bytes(&PoolExpr) / BYTES_PER_KB,
|
||||||
|
FixedMemoryPool__total_bytes(&PoolExpr) / BYTES_PER_KB,
|
||||||
|
PoolExpr.exceeded_bytes / BYTES_PER_KB
|
||||||
|
);
|
||||||
|
buffer += n; size -= n;
|
||||||
|
n = snprintf(
|
||||||
|
buffer, size, "PoolFrame: %.2f KB (used) / %.2f KB (total) - %.2f KB (exceeded)\n",
|
||||||
|
FixedMemoryPool__used_bytes(&PoolFrame) / BYTES_PER_KB,
|
||||||
|
FixedMemoryPool__total_bytes(&PoolFrame) / BYTES_PER_KB,
|
||||||
|
PoolFrame.exceeded_bytes / BYTES_PER_KB
|
||||||
|
);
|
||||||
|
buffer += n; size -= n;
|
||||||
|
// PoolObject
|
||||||
|
int empty_arenas = PoolObject._empty_arenas.count;
|
||||||
|
int arenas = PoolObject._arenas.count;
|
||||||
|
// print empty arenas count
|
||||||
|
n = snprintf(
|
||||||
|
buffer, size, "PoolObject: %d empty arenas, %d arenas\n",
|
||||||
|
empty_arenas, arenas
|
||||||
|
);
|
||||||
|
buffer += n; size -= n;
|
||||||
|
// log each non-empty arena
|
||||||
|
LinkedList__apply(&PoolObject._arenas,
|
||||||
|
MemoryPoolArena* arena = (MemoryPoolArena*)node;
|
||||||
|
n = snprintf(
|
||||||
|
buffer, size, " - %p: %.2f MB (used) / %.2f MB (total)\n",
|
||||||
|
(void*)arena,
|
||||||
|
MemoryPoolArena__used_bytes(arena) / BYTES_PER_MB,
|
||||||
|
MemoryPoolArena__total_bytes(arena) / BYTES_PER_MB
|
||||||
|
);
|
||||||
|
buffer += n; size -= n;
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,297 +0,0 @@
|
|||||||
#include "pocketpy/common/memorypool.hpp"
|
|
||||||
#include "pocketpy/common/config.h"
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace pkpy {
|
|
||||||
|
|
||||||
struct LinkedListNode {
|
|
||||||
LinkedListNode* prev;
|
|
||||||
LinkedListNode* next;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct DoubleLinkedList {
|
|
||||||
static_assert(std::is_base_of_v<LinkedListNode, T>);
|
|
||||||
int _size;
|
|
||||||
LinkedListNode head;
|
|
||||||
LinkedListNode tail;
|
|
||||||
|
|
||||||
DoubleLinkedList() : _size(0) {
|
|
||||||
head.prev = nullptr;
|
|
||||||
head.next = &tail;
|
|
||||||
tail.prev = &head;
|
|
||||||
tail.next = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void push_back(T* node) {
|
|
||||||
node->prev = tail.prev;
|
|
||||||
node->next = &tail;
|
|
||||||
tail.prev->next = node;
|
|
||||||
tail.prev = node;
|
|
||||||
_size++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void push_front(T* node) {
|
|
||||||
node->prev = &head;
|
|
||||||
node->next = head.next;
|
|
||||||
head.next->prev = node;
|
|
||||||
head.next = node;
|
|
||||||
_size++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop_back() {
|
|
||||||
assert(!empty());
|
|
||||||
tail.prev->prev->next = &tail;
|
|
||||||
tail.prev = tail.prev->prev;
|
|
||||||
_size--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop_front() {
|
|
||||||
assert(!empty());
|
|
||||||
head.next->next->prev = &head;
|
|
||||||
head.next = head.next->next;
|
|
||||||
_size--;
|
|
||||||
}
|
|
||||||
|
|
||||||
T* back() const {
|
|
||||||
assert(!empty());
|
|
||||||
return static_cast<T*>(tail.prev);
|
|
||||||
}
|
|
||||||
|
|
||||||
T* front() const {
|
|
||||||
assert(!empty());
|
|
||||||
return static_cast<T*>(head.next);
|
|
||||||
}
|
|
||||||
|
|
||||||
void erase(T* node) {
|
|
||||||
node->prev->next = node->next;
|
|
||||||
node->next->prev = node->prev;
|
|
||||||
_size--;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const { return _size == 0; }
|
|
||||||
|
|
||||||
int size() const { return _size; }
|
|
||||||
|
|
||||||
template <typename Func>
|
|
||||||
void apply(Func func) {
|
|
||||||
LinkedListNode* p = head.next;
|
|
||||||
while(p != &tail) {
|
|
||||||
LinkedListNode* next = p->next;
|
|
||||||
func(static_cast<T*>(p));
|
|
||||||
p = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MemoryPool {
|
|
||||||
const static int __BlockSize = kPoolObjectBlockSize;
|
|
||||||
const static int __MaxBlocks = 256 * 1024 / __BlockSize;
|
|
||||||
const static int __MinArenaCount = PK_GC_MIN_THRESHOLD * 100 / (256 * 1024);
|
|
||||||
|
|
||||||
struct Block {
|
|
||||||
void* arena;
|
|
||||||
char data[__BlockSize];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Arena : LinkedListNode {
|
|
||||||
Block _blocks[__MaxBlocks];
|
|
||||||
Block* _free_list[__MaxBlocks];
|
|
||||||
int _free_list_size;
|
|
||||||
|
|
||||||
Arena() : _free_list_size(__MaxBlocks) {
|
|
||||||
for(int i = 0; i < __MaxBlocks; i++) {
|
|
||||||
_blocks[i].arena = this;
|
|
||||||
_free_list[i] = &_blocks[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const { return _free_list_size == 0; }
|
|
||||||
|
|
||||||
bool full() const { return _free_list_size == __MaxBlocks; }
|
|
||||||
|
|
||||||
int total_bytes() const { return __BlockSize * __MaxBlocks; }
|
|
||||||
int used_bytes() const { return __BlockSize * (__MaxBlocks - _free_list_size); }
|
|
||||||
|
|
||||||
Block* alloc() {
|
|
||||||
assert(!empty());
|
|
||||||
_free_list_size--;
|
|
||||||
return _free_list[_free_list_size];
|
|
||||||
}
|
|
||||||
|
|
||||||
void dealloc(Block* block) {
|
|
||||||
assert(!full());
|
|
||||||
_free_list[_free_list_size] = block;
|
|
||||||
_free_list_size++;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
MemoryPool() = default;
|
|
||||||
MemoryPool(const MemoryPool&) = delete;
|
|
||||||
MemoryPool& operator= (const MemoryPool&) = delete;
|
|
||||||
MemoryPool(MemoryPool&&) = delete;
|
|
||||||
MemoryPool& operator= (MemoryPool&&) = delete;
|
|
||||||
|
|
||||||
DoubleLinkedList<Arena> _arenas;
|
|
||||||
DoubleLinkedList<Arena> _empty_arenas;
|
|
||||||
|
|
||||||
void* alloc() {
|
|
||||||
PK_GLOBAL_SCOPE_LOCK();
|
|
||||||
if(_arenas.empty()) { _arenas.push_back(new Arena()); }
|
|
||||||
Arena* arena = _arenas.back();
|
|
||||||
void* p = arena->alloc()->data;
|
|
||||||
if(arena->empty()) {
|
|
||||||
_arenas.pop_back();
|
|
||||||
_empty_arenas.push_back(arena);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dealloc(void* p) {
|
|
||||||
PK_GLOBAL_SCOPE_LOCK();
|
|
||||||
assert(p != nullptr);
|
|
||||||
Block* block = (Block*)((char*)p - sizeof(void*));
|
|
||||||
assert(block->arena != nullptr);
|
|
||||||
Arena* arena = (Arena*)block->arena;
|
|
||||||
if(arena->empty()) {
|
|
||||||
_empty_arenas.erase(arena);
|
|
||||||
_arenas.push_front(arena);
|
|
||||||
arena->dealloc(block);
|
|
||||||
} else {
|
|
||||||
arena->dealloc(block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void shrink_to_fit() {
|
|
||||||
PK_GLOBAL_SCOPE_LOCK();
|
|
||||||
if(_arenas.size() < __MinArenaCount) return;
|
|
||||||
_arenas.apply([this](Arena* arena) {
|
|
||||||
if(arena->full()) {
|
|
||||||
_arenas.erase(arena);
|
|
||||||
delete arena;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
~MemoryPool() {
|
|
||||||
_arenas.apply([](Arena* arena) {
|
|
||||||
delete arena;
|
|
||||||
});
|
|
||||||
_empty_arenas.apply([](Arena* arena) {
|
|
||||||
delete arena;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <int BlockSize, int BlockCount>
|
|
||||||
struct FixedMemoryPool {
|
|
||||||
struct Block {
|
|
||||||
char data[BlockSize];
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(BlockSize % 4 == 0);
|
|
||||||
static_assert(sizeof(Block) == BlockSize);
|
|
||||||
|
|
||||||
Block _blocks[BlockCount];
|
|
||||||
Block* _free_list[BlockCount];
|
|
||||||
Block** _free_list_end;
|
|
||||||
int _exceeded_bytes;
|
|
||||||
|
|
||||||
int total_bytes() const { return BlockSize * BlockCount; }
|
|
||||||
int used_bytes() const { return (BlockCount - (_free_list_end - _free_list)) * BlockSize; }
|
|
||||||
int exceeded_bytes() const { return _exceeded_bytes; }
|
|
||||||
|
|
||||||
FixedMemoryPool() {
|
|
||||||
_exceeded_bytes = 0;
|
|
||||||
_free_list_end = _free_list + BlockCount;
|
|
||||||
for(int i = 0; i < BlockCount; ++i) {
|
|
||||||
_free_list[i] = _blocks + i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_valid(void* p) { return p >= _blocks && p < _blocks + BlockCount; }
|
|
||||||
|
|
||||||
void* alloc() {
|
|
||||||
PK_GLOBAL_SCOPE_LOCK()
|
|
||||||
if(_free_list_end != _free_list) {
|
|
||||||
--_free_list_end;
|
|
||||||
return *_free_list_end;
|
|
||||||
} else {
|
|
||||||
_exceeded_bytes += BlockSize;
|
|
||||||
return std::malloc(BlockSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dealloc(void* p) {
|
|
||||||
PK_GLOBAL_SCOPE_LOCK()
|
|
||||||
if(is_valid(p)) {
|
|
||||||
*_free_list_end = static_cast<Block*>(p);
|
|
||||||
++_free_list_end;
|
|
||||||
} else {
|
|
||||||
_exceeded_bytes -= BlockSize;
|
|
||||||
std::free(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static FixedMemoryPool<kPoolExprBlockSize, 64> PoolExpr;
|
|
||||||
static FixedMemoryPool<kPoolFrameBlockSize, 128> PoolFrame;
|
|
||||||
static MemoryPool PoolObject;
|
|
||||||
|
|
||||||
void* PoolExpr_alloc() noexcept { return PoolExpr.alloc(); }
|
|
||||||
|
|
||||||
void PoolExpr_dealloc(void* p) noexcept { PoolExpr.dealloc(p); }
|
|
||||||
|
|
||||||
void* PoolFrame_alloc() noexcept { return PoolFrame.alloc(); }
|
|
||||||
|
|
||||||
void PoolFrame_dealloc(void* p) noexcept { PoolFrame.dealloc(p); }
|
|
||||||
|
|
||||||
void* PoolObject_alloc() noexcept { return PoolObject.alloc(); }
|
|
||||||
|
|
||||||
void PoolObject_dealloc(void* p) noexcept { PoolObject.dealloc(p); }
|
|
||||||
|
|
||||||
void PoolObject_shrink_to_fit() noexcept { PoolObject.shrink_to_fit(); }
|
|
||||||
|
|
||||||
void Pools_debug_info(char* buffer, int size) noexcept {
|
|
||||||
double BYTES_PER_MB = 1024.0f * 1024.0f;
|
|
||||||
double BYTES_PER_KB = 1024.0f;
|
|
||||||
int n = 0;
|
|
||||||
n = snprintf(
|
|
||||||
buffer, size, "PoolExpr: %.2f KB (used) / %.2f KB (total) - %.2f KB (exceeded)\n",
|
|
||||||
PoolExpr.used_bytes() / BYTES_PER_KB,
|
|
||||||
PoolExpr.total_bytes() / BYTES_PER_KB,
|
|
||||||
PoolExpr.exceeded_bytes() / BYTES_PER_KB
|
|
||||||
);
|
|
||||||
buffer += n; size -= n;
|
|
||||||
n = snprintf(
|
|
||||||
buffer, size, "PoolFrame: %.2f KB (used) / %.2f KB (total) - %.2f KB (exceeded)\n",
|
|
||||||
PoolFrame.used_bytes() / BYTES_PER_KB,
|
|
||||||
PoolFrame.total_bytes() / BYTES_PER_KB,
|
|
||||||
PoolFrame.exceeded_bytes() / BYTES_PER_KB
|
|
||||||
);
|
|
||||||
buffer += n; size -= n;
|
|
||||||
// PoolObject
|
|
||||||
int empty_arenas = PoolObject._empty_arenas.size();
|
|
||||||
int arenas = PoolObject._arenas.size();
|
|
||||||
// print empty arenas count
|
|
||||||
n = snprintf(
|
|
||||||
buffer, size, "PoolObject: %d empty arenas, %d arenas\n",
|
|
||||||
empty_arenas, arenas
|
|
||||||
);
|
|
||||||
buffer += n; size -= n;
|
|
||||||
// log each non-empty arena
|
|
||||||
PoolObject._arenas.apply([&](MemoryPool::Arena* arena) {
|
|
||||||
n = snprintf(
|
|
||||||
buffer, size, " - %p: %.2f MB (used) / %.2f MB (total)\n",
|
|
||||||
(void*)arena,
|
|
||||||
arena->used_bytes() / BYTES_PER_MB,
|
|
||||||
arena->total_bytes() / BYTES_PER_MB
|
|
||||||
);
|
|
||||||
buffer += n; size -= n;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pkpy
|
|
@ -1,5 +1,4 @@
|
|||||||
#include "pocketpy/common/str.hpp"
|
#include "pocketpy/common/str.hpp"
|
||||||
#include "pocketpy/common/gil.hpp"
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
@ -39,7 +38,7 @@ const char* StrName::c_str() const { return _r_interned()[index].c_str(); }
|
|||||||
uint32_t StrName::_pesudo_random_index = 0;
|
uint32_t StrName::_pesudo_random_index = 0;
|
||||||
|
|
||||||
StrName StrName::get(std::string_view s) {
|
StrName StrName::get(std::string_view s) {
|
||||||
PK_GLOBAL_SCOPE_LOCK()
|
// TODO: PK_GLOBAL_SCOPE_LOCK()
|
||||||
auto it = _interned().find(s);
|
auto it = _interned().find(s);
|
||||||
if(it != _interned().end()) return StrName(it->second);
|
if(it != _interned().end()) return StrName(it->second);
|
||||||
// generate new index
|
// generate new index
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include "pocketpy/compiler/lexer.hpp"
|
#include "pocketpy/compiler/lexer.hpp"
|
||||||
#include "pocketpy/common/gil.hpp"
|
#include "pocketpy/common/config.h"
|
||||||
#include "pocketpy/common/version.h"
|
|
||||||
#include "pocketpy/common/str.h"
|
#include "pocketpy/common/str.h"
|
||||||
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "pocketpy/interpreter/vm.hpp"
|
#include "pocketpy/interpreter/vm.hpp"
|
||||||
|
#include "pocketpy/common/memorypool.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@ -81,6 +82,8 @@ struct JsonSerializer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
VM::VM(bool enable_os) : heap(this), enable_os(enable_os) {
|
VM::VM(bool enable_os) : heap(this), enable_os(enable_os) {
|
||||||
|
Pools_initialize();
|
||||||
|
|
||||||
this->vm = this;
|
this->vm = this;
|
||||||
this->__c.error = nullptr;
|
this->__c.error = nullptr;
|
||||||
_ceval_on_step = nullptr;
|
_ceval_on_step = nullptr;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include "pocketpy/modules/modules.hpp"
|
#include "pocketpy/modules/modules.hpp"
|
||||||
#include "pocketpy/interpreter/bindings.hpp"
|
#include "pocketpy/interpreter/bindings.hpp"
|
||||||
#include "pocketpy/common/version.h"
|
|
||||||
#include "pocketpy/common/export.h"
|
#include "pocketpy/common/export.h"
|
||||||
|
|
||||||
#include "pocketpy/common/_generated.h"
|
#include "pocketpy/common/_generated.h"
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
#include "pocketpy/tools/repl.hpp"
|
#include "pocketpy/tools/repl.hpp"
|
||||||
|
|
||||||
#include "pocketpy/common/version.h"
|
|
||||||
#include "pocketpy/common/export.h"
|
#include "pocketpy/common/export.h"
|
||||||
|
|
||||||
namespace pkpy {
|
namespace pkpy {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user