diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 14a340ffee37..8b9e05e83160 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -191,6 +191,10 @@ ZEND_API const zend_internal_function zend_pass_function = { ZEND_API void zend_vm_stack_init(void) { EG(vm_stack_page_size) = ZEND_VM_STACK_PAGE_SIZE; + EG(vm_stack_page_cache) = NULL; + EG(vm_stack_page_cache_count) = 0; + EG(fiber_vm_stack_page_cache) = NULL; + EG(fiber_vm_stack_page_cache_count) = 0; EG(vm_stack) = zend_vm_stack_new_page(ZEND_VM_STACK_PAGE_SIZE, NULL); EG(vm_stack_top) = EG(vm_stack)->top; EG(vm_stack_end) = EG(vm_stack)->end; @@ -201,6 +205,10 @@ ZEND_API void zend_vm_stack_init_ex(size_t page_size) /* page_size must be a power of 2 */ ZEND_ASSERT(page_size > 0 && (page_size & (page_size - 1)) == 0); EG(vm_stack_page_size) = page_size; + EG(vm_stack_page_cache) = NULL; + EG(vm_stack_page_cache_count) = 0; + EG(fiber_vm_stack_page_cache) = NULL; + EG(fiber_vm_stack_page_cache_count) = 0; EG(vm_stack) = zend_vm_stack_new_page(page_size, NULL); EG(vm_stack_top) = EG(vm_stack)->top; EG(vm_stack_end) = EG(vm_stack)->end; @@ -217,6 +225,23 @@ ZEND_API void zend_vm_stack_destroy(void) } } +ZEND_API void zend_vm_stack_destroy_caches(void) +{ + while (EG(vm_stack_page_cache) != NULL) { + zend_vm_stack cached = EG(vm_stack_page_cache); + EG(vm_stack_page_cache) = cached->prev; + efree(cached); + } + EG(vm_stack_page_cache_count) = 0; + + while (EG(fiber_vm_stack_page_cache) != NULL) { + zend_vm_stack cached = EG(fiber_vm_stack_page_cache); + EG(fiber_vm_stack_page_cache) = cached->prev; + efree(cached); + } + EG(fiber_vm_stack_page_cache_count) = 0; +} + ZEND_API void* zend_vm_stack_extend(size_t size) { zend_vm_stack stack; diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 017c8d208a14..2250a873af29 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -322,10 +322,41 @@ ZEND_STATIC_ASSERT(ZEND_MM_ALIGNED_SIZE(sizeof(zval)) == sizeof(zval), ZEND_API void zend_vm_stack_init(void); ZEND_API void zend_vm_stack_init_ex(size_t page_size); ZEND_API void zend_vm_stack_destroy(void); +ZEND_API void zend_vm_stack_destroy_caches(void); ZEND_API void* zend_vm_stack_extend(size_t size); +#define ZEND_FIBER_VM_STACK_SIZE (1024 * sizeof(zval)) + +static zend_always_inline zend_vm_stack zend_vm_stack_cached_page(size_t size) { + zend_vm_stack page; + + if (size == ZEND_FIBER_VM_STACK_SIZE) { + page = EG(fiber_vm_stack_page_cache); + if (page) { + ZEND_ASSERT((size_t)((char*)page->end - (char*)page) == size); + EG(fiber_vm_stack_page_cache) = page->prev; + EG(fiber_vm_stack_page_cache_count)--; + return page; + } + } else { + page = EG(vm_stack_page_cache); + ZEND_ASSERT(!page || ((size_t)((char*)page->end - (char*)page) == size) || size != EG(vm_stack_page_size)); + if (page && EXPECTED((size_t)((char*)page->end - (char*)page) == size)) { + EG(vm_stack_page_cache) = page->prev; + EG(vm_stack_page_cache_count)--; + return page; + } + } + + return NULL; +} + static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend_vm_stack prev) { - zend_vm_stack page = (zend_vm_stack)emalloc(size); + zend_vm_stack page = zend_vm_stack_cached_page(size); + + if (!page) { + page = (zend_vm_stack)emalloc(size); + } page->top = ZEND_VM_STACK_ELEMENTS(page); page->end = (zval*)((char*)page + size); @@ -421,7 +452,24 @@ static zend_always_inline void zend_vm_stack_free_call_frame_ex(uint32_t call_in EG(vm_stack_top) = prev->top; EG(vm_stack_end) = prev->end; EG(vm_stack) = prev; - efree(p); + if ((size_t)((char*)p->end - (char*)p) == ZEND_FIBER_VM_STACK_SIZE) { + if (EG(fiber_vm_stack_page_cache_count) < 32) { + p->prev = EG(fiber_vm_stack_page_cache); + EG(fiber_vm_stack_page_cache) = p; + EG(fiber_vm_stack_page_cache_count)++; + } else { + efree(p); + } + } else { + if (EG(vm_stack_page_cache_count) < 32 + && (size_t)((char*)p->end - (char*)p) == EG(vm_stack_page_size)) { + p->prev = EG(vm_stack_page_cache); + EG(vm_stack_page_cache) = p; + EG(vm_stack_page_cache_count)++; + } else { + efree(p); + } + } } else { EG(vm_stack_top) = (zval*)call; } diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index b48352a5aaf3..c10e88c12118 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -482,6 +482,7 @@ void shutdown_executor(void) /* {{{ */ zend_hash_discard(EG(class_table), EG(persistent_classes_count)); } else { zend_vm_stack_destroy(); + zend_vm_stack_destroy_caches(); if (EG(full_tables_cleanup)) { zend_hash_reverse_apply(EG(function_table), clean_non_persistent_function_full); diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index c72ffdc8f18e..ec495c9ab9e3 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -25,7 +25,6 @@ #define ZEND_FIBER_GUARD_PAGES 1 #define ZEND_FIBER_DEFAULT_C_STACK_SIZE (4096 * (((sizeof(void *)) < 8) ? 256 : 512)) -#define ZEND_FIBER_VM_STACK_SIZE (1024 * sizeof(zval)) BEGIN_EXTERN_C() diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 4d5e300e2859..7c68c780387e 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -202,6 +202,11 @@ struct _zend_executor_globals { zval *vm_stack_end; zend_vm_stack vm_stack; size_t vm_stack_page_size; + zend_vm_stack vm_stack_page_cache; + uint32_t vm_stack_page_cache_count; + /* Fibers use a different page size, so they need a separate cache */ + zend_vm_stack fiber_vm_stack_page_cache; + uint32_t fiber_vm_stack_page_cache_count; struct _zend_execute_data *current_execute_data; const zend_class_entry *fake_scope; /* used to avoid checks accessing properties */