Skip to content

Commit 5b7b07d

Browse files
committed
ext/opcache: remove option huge_code_pages
This feature is useless at best, but is fragile and increases memory usage because it creates a copy of the PHP executable in RAM, when another copy is already in the kernel's page cache. Additionally, each PHP process has its own copy, multiplying the memory usage with the number of PHP processes. Even worse: under memory pressure, the kernel would usually just discard those code pages, and reload them from disk on demand; but with `huge_code_pages`, the kernel cannot simply discard those pages, instead it has to allocate room in the swap partition and then has to write the pages to the swap partition before being able to discard them. This adds disk I/O to an already extremely constrained situation. The feature was added 7 years ago in commit 669f0b3, and the commit message stated that this "provided 2% improvement on real-life apps, because of 2-3 times reduction in number of iTLB misses", but did not tell how these results were produced. I tried, but failed to measure a difference. None of my tests went any faster with `huge_code_pages` enabled. In a second attempt, I tried `perf stat` with events `itlb_misses.miss_causes_a_walk`, `itlb_misses.stlb_hit`, `itlb_misses.walk_completed`, `itlb_misses.walk_duration` and could not see any improvements here either. The results were unstable, but nothing suggested it was better with `huge_code_pages`. Quite contrary, this operation delays PHP startup by 5 ms (measured on an Intel Xeon E5-2630). phpinfo() with opcache.huge_code_pages=0: 17.92 msec task-clock:u # 0.985 CPUs utilized 29,804,809 cycles:u # 1.663 GHz 44,512,668 instructions:u # 1.49 insn per cycle phpinfo() with opcache.huge_code_pages=1: 23.20 msec task-clock:u # 0.988 CPUs utilized 35,709,593 cycles:u # 1.539 GHz 45,940,779 instructions:u # 1.29 insn per cycle In order to reduce PHP's memory size, reduce the load during PHP startup, simplify debugging and profiling, I propose removing this feature.
1 parent 995ac31 commit 5b7b07d

8 files changed

+2
-206
lines changed

ext/opcache/ZendAccelerator.c

-179
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
#include "zend_vm.h"
3535
#include "zend_inheritance.h"
3636
#include "zend_exceptions.h"
37-
#include "zend_mmap.h"
3837
#include "zend_observer.h"
3938
#include "main/php_main.h"
4039
#include "main/SAPI.h"
@@ -2949,174 +2948,6 @@ static void accel_globals_ctor(zend_accel_globals *accel_globals)
29492948
memset(accel_globals, 0, sizeof(zend_accel_globals));
29502949
}
29512950

2952-
#ifdef HAVE_HUGE_CODE_PAGES
2953-
# ifndef _WIN32
2954-
# include <sys/mman.h>
2955-
# ifndef MAP_ANON
2956-
# ifdef MAP_ANONYMOUS
2957-
# define MAP_ANON MAP_ANONYMOUS
2958-
# endif
2959-
# endif
2960-
# ifndef MAP_FAILED
2961-
# define MAP_FAILED ((void*)-1)
2962-
# endif
2963-
# ifdef MAP_ALIGNED_SUPER
2964-
# include <sys/types.h>
2965-
# include <sys/sysctl.h>
2966-
# include <sys/user.h>
2967-
# define MAP_HUGETLB MAP_ALIGNED_SUPER
2968-
# endif
2969-
# endif
2970-
2971-
# if defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE)
2972-
static zend_result accel_remap_huge_pages(void *start, size_t size, size_t real_size, const char *name, size_t offset)
2973-
{
2974-
void *ret = MAP_FAILED;
2975-
void *mem;
2976-
2977-
mem = mmap(NULL, size,
2978-
PROT_READ | PROT_WRITE,
2979-
MAP_PRIVATE | MAP_ANONYMOUS,
2980-
-1, 0);
2981-
if (mem == MAP_FAILED) {
2982-
zend_error(E_WARNING,
2983-
ACCELERATOR_PRODUCT_NAME " huge_code_pages: mmap failed: %s (%d)",
2984-
strerror(errno), errno);
2985-
return FAILURE;
2986-
}
2987-
memcpy(mem, start, real_size);
2988-
2989-
# ifdef MAP_HUGETLB
2990-
ret = mmap(start, size,
2991-
PROT_READ | PROT_WRITE | PROT_EXEC,
2992-
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_HUGETLB,
2993-
-1, 0);
2994-
# endif
2995-
if (ret == MAP_FAILED) {
2996-
ret = mmap(start, size,
2997-
PROT_READ | PROT_WRITE | PROT_EXEC,
2998-
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
2999-
-1, 0);
3000-
/* this should never happen? */
3001-
ZEND_ASSERT(ret != MAP_FAILED);
3002-
# ifdef MADV_HUGEPAGE
3003-
if (-1 == madvise(start, size, MADV_HUGEPAGE)) {
3004-
memcpy(start, mem, real_size);
3005-
mprotect(start, size, PROT_READ | PROT_EXEC);
3006-
munmap(mem, size);
3007-
zend_error(E_WARNING,
3008-
ACCELERATOR_PRODUCT_NAME " huge_code_pages: madvise(HUGEPAGE) failed: %s (%d)",
3009-
strerror(errno), errno);
3010-
return FAILURE;
3011-
}
3012-
# else
3013-
memcpy(start, mem, real_size);
3014-
mprotect(start, size, PROT_READ | PROT_EXEC);
3015-
munmap(mem, size);
3016-
zend_error(E_WARNING,
3017-
ACCELERATOR_PRODUCT_NAME " huge_code_pages: mmap(HUGETLB) failed: %s (%d)",
3018-
strerror(errno), errno);
3019-
return FAILURE;
3020-
# endif
3021-
}
3022-
3023-
// Given the MAP_FIXED flag the address can never diverge
3024-
ZEND_ASSERT(ret == start);
3025-
zend_mmap_set_name(start, size, "zend_huge_code_pages");
3026-
memcpy(start, mem, real_size);
3027-
mprotect(start, size, PROT_READ | PROT_EXEC);
3028-
3029-
munmap(mem, size);
3030-
3031-
return SUCCESS;
3032-
}
3033-
3034-
static void accel_move_code_to_huge_pages(void)
3035-
{
3036-
#if defined(__linux__)
3037-
FILE *f;
3038-
long unsigned int huge_page_size = 2 * 1024 * 1024;
3039-
3040-
f = fopen("/proc/self/maps", "r");
3041-
if (f) {
3042-
long unsigned int start, end, offset, inode;
3043-
char perm[5], dev[10], name[MAXPATHLEN];
3044-
int ret;
3045-
3046-
while (1) {
3047-
ret = fscanf(f, "%lx-%lx %4s %lx %9s %ld %s\n", &start, &end, perm, &offset, dev, &inode, name);
3048-
if (ret == 7) {
3049-
if (perm[0] == 'r' && perm[1] == '-' && perm[2] == 'x' && name[0] == '/') {
3050-
long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size);
3051-
long unsigned int seg_end = (end & ~(huge_page_size-1L));
3052-
long unsigned int real_end;
3053-
3054-
ret = fscanf(f, "%lx-", &start);
3055-
if (ret == 1 && start == seg_end + huge_page_size) {
3056-
real_end = end;
3057-
seg_end = start;
3058-
} else {
3059-
real_end = seg_end;
3060-
}
3061-
3062-
if (seg_end > seg_start) {
3063-
zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, name);
3064-
accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, real_end - seg_start, name, offset + seg_start - start);
3065-
}
3066-
break;
3067-
}
3068-
} else {
3069-
break;
3070-
}
3071-
}
3072-
fclose(f);
3073-
}
3074-
#elif defined(__FreeBSD__)
3075-
size_t s = 0;
3076-
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()};
3077-
long unsigned int huge_page_size = 2 * 1024 * 1024;
3078-
if (sysctl(mib, 4, NULL, &s, NULL, 0) == 0) {
3079-
s = s * 4 / 3;
3080-
void *addr = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
3081-
if (addr != MAP_FAILED) {
3082-
if (sysctl(mib, 4, addr, &s, NULL, 0) == 0) {
3083-
uintptr_t start = (uintptr_t)addr;
3084-
uintptr_t end = start + s;
3085-
while (start < end) {
3086-
struct kinfo_vmentry *entry = (struct kinfo_vmentry *)start;
3087-
size_t sz = entry->kve_structsize;
3088-
if (sz == 0) {
3089-
break;
3090-
}
3091-
int permflags = entry->kve_protection;
3092-
if ((permflags & KVME_PROT_READ) && !(permflags & KVME_PROT_WRITE) &&
3093-
(permflags & KVME_PROT_EXEC) && entry->kve_path[0] != '\0') {
3094-
long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size);
3095-
long unsigned int seg_end = (end & ~(huge_page_size-1L));
3096-
if (seg_end > seg_start) {
3097-
zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, entry->kve_path);
3098-
accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, seg_end - seg_start, entry->kve_path, entry->kve_offset + seg_start - start);
3099-
// First relevant segment found is our binary
3100-
break;
3101-
}
3102-
}
3103-
start += sz;
3104-
}
3105-
}
3106-
munmap(addr, s);
3107-
}
3108-
}
3109-
#endif
3110-
}
3111-
# else
3112-
static void accel_move_code_to_huge_pages(void)
3113-
{
3114-
zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages has no affect as huge page is not supported");
3115-
return;
3116-
}
3117-
# endif /* defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE) */
3118-
#endif /* HAVE_HUGE_CODE_PAGES */
3119-
31202951
static int accel_startup(zend_extension *extension)
31212952
{
31222953
#ifdef ZTS
@@ -3148,16 +2979,6 @@ static int accel_startup(zend_extension *extension)
31482979
}
31492980
#endif
31502981

3151-
#ifdef HAVE_HUGE_CODE_PAGES
3152-
if (ZCG(accel_directives).huge_code_pages &&
3153-
(strcmp(sapi_module.name, "cli") == 0 ||
3154-
strcmp(sapi_module.name, "cli-server") == 0 ||
3155-
strcmp(sapi_module.name, "cgi-fcgi") == 0 ||
3156-
strcmp(sapi_module.name, "fpm-fcgi") == 0)) {
3157-
accel_move_code_to_huge_pages();
3158-
}
3159-
#endif
3160-
31612982
/* no supported SAPI found - disable acceleration and stop initialization */
31622983
if (accel_find_sapi() == FAILURE) {
31632984
accel_startup_ok = false;

ext/opcache/ZendAccelerator.h

-3
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,6 @@ typedef struct _zend_accel_directives {
186186
bool file_cache_consistency_checks;
187187
#if ENABLE_FILE_CACHE_FALLBACK
188188
bool file_cache_fallback;
189-
#endif
190-
#ifdef HAVE_HUGE_CODE_PAGES
191-
bool huge_code_pages;
192189
#endif
193190
char *preload;
194191
#ifndef ZEND_WIN32

ext/opcache/config.m4

-7
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,6 @@ PHP_ARG_ENABLE([opcache],
44
[Disable Zend OPcache support])],
55
[yes])
66

7-
PHP_ARG_ENABLE([huge-code-pages],
8-
[whether to enable copying PHP CODE pages into HUGE PAGES],
9-
[AS_HELP_STRING([--disable-huge-code-pages],
10-
[Disable copying PHP CODE pages into HUGE PAGES])],
11-
[yes],
12-
[no])
13-
147
PHP_ARG_ENABLE([opcache-jit],
158
[whether to enable JIT],
169
[AS_HELP_STRING([--disable-opcache-jit],

ext/opcache/jit/zend_jit_perf_dump.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ extern unsigned int thr_self(void);
5252
/*
5353
* 1) Profile using perf-<pid>.map
5454
*
55-
* perf record php -d opcache.huge_code_pages=0 -d opcache.jit_debug=0x10 bench.php
55+
* perf record php -d opcache.jit_debug=0x10 bench.php
5656
* perf report
5757
*
5858
* 2) Profile using jit-<pid>.dump
5959
*
60-
* perf record -k 1 php -d opcache.huge_code_pages=0 -d opcache.jit_debug=0x20 bench.php
60+
* perf record -k 1 php -d opcache.jit_debug=0x20 bench.php
6161
* perf inject -j -i perf.data -o perf.data.jitted
6262
* perf report -i perf.data.jitted
6363
*

ext/opcache/tests/zzz_basic_logging.phpt

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ opcache.enable_cli=1
99
opcache.file_cache_only=0
1010
opcache.error_log=
1111
opcache.log_verbosity_level=4
12-
opcache.huge_code_pages=0
1312
opcache.preload=
1413
opcache.interned_strings_buffer=8
1514
--EXTENSIONS--

ext/opcache/zend_accelerator_module.c

-6
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,6 @@ ZEND_INI_BEGIN()
293293
STD_PHP_INI_BOOLEAN("opcache.file_cache_consistency_checks" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_consistency_checks, zend_accel_globals, accel_globals)
294294
#if ENABLE_FILE_CACHE_FALLBACK
295295
STD_PHP_INI_BOOLEAN("opcache.file_cache_fallback" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_fallback, zend_accel_globals, accel_globals)
296-
#endif
297-
#ifdef HAVE_HUGE_CODE_PAGES
298-
STD_PHP_INI_BOOLEAN("opcache.huge_code_pages" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.huge_code_pages, zend_accel_globals, accel_globals)
299296
#endif
300297
STD_PHP_INI_ENTRY("opcache.preload" , "" , PHP_INI_SYSTEM, OnUpdateStringUnempty, accel_directives.preload, zend_accel_globals, accel_globals)
301298
#ifndef ZEND_WIN32
@@ -807,9 +804,6 @@ ZEND_FUNCTION(opcache_get_configuration)
807804
add_assoc_long(&directives, "opcache.file_update_protection", ZCG(accel_directives).file_update_protection);
808805
add_assoc_long(&directives, "opcache.opt_debug_level", ZCG(accel_directives).opt_debug_level);
809806
add_assoc_string(&directives, "opcache.restrict_api", STRING_NOT_NULL(ZCG(accel_directives).restrict_api));
810-
#ifdef HAVE_HUGE_CODE_PAGES
811-
add_assoc_bool(&directives, "opcache.huge_code_pages", ZCG(accel_directives).huge_code_pages);
812-
#endif
813807
add_assoc_string(&directives, "opcache.preload", STRING_NOT_NULL(ZCG(accel_directives).preload));
814808
#ifndef ZEND_WIN32
815809
add_assoc_string(&directives, "opcache.preload_user", STRING_NOT_NULL(ZCG(accel_directives).preload_user));

php.ini-development

-4
Original file line numberDiff line numberDiff line change
@@ -1909,10 +1909,6 @@ ldap.max_links = -1
19091909
; cache is required.
19101910
;opcache.file_cache_fallback=1
19111911

1912-
; Enables or disables copying of PHP code (text segment) into HUGE PAGES.
1913-
; This should improve performance, but requires appropriate OS configuration.
1914-
;opcache.huge_code_pages=0
1915-
19161912
; Validate cached file permissions.
19171913
;opcache.validate_permission=0
19181914

php.ini-production

-4
Original file line numberDiff line numberDiff line change
@@ -1911,10 +1911,6 @@ ldap.max_links = -1
19111911
; cache is required.
19121912
;opcache.file_cache_fallback=1
19131913

1914-
; Enables or disables copying of PHP code (text segment) into HUGE PAGES.
1915-
; This should improve performance, but requires appropriate OS configuration.
1916-
;opcache.huge_code_pages=1
1917-
19181914
; Validate cached file permissions.
19191915
;opcache.validate_permission=0
19201916

0 commit comments

Comments
 (0)