Skip to content

Commit 669f0b3

Browse files
committed
Added an experemental ability to move PHP code pages (PHP TEXT segment) into HUGE pages.
PHP should be configured and built with --enable-huge-code-pages, OS should be configured to provide huge pages. It's possible to enable/disable this future in php.ini through opcache.enable_huge_code_pages=0/1. The feature was tested on Linux and provided 2% improvement on real-life apps, because of 2-3 times reduction in number of iTLB misses.
1 parent 145708b commit 669f0b3

File tree

4 files changed

+109
-0
lines changed

4 files changed

+109
-0
lines changed

ext/opcache/ZendAccelerator.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2480,6 +2480,92 @@ static void accel_gen_system_id(void)
24802480
}
24812481
#endif
24822482

2483+
#ifdef HAVE_HUGE_CODE_PAGES
2484+
# ifndef _WIN32
2485+
# include <sys/mman.h>
2486+
# include <sys/prctl.h>
2487+
# ifndef MAP_ANON
2488+
# ifdef MAP_ANONYMOUS
2489+
# define MAP_ANON MAP_ANONYMOUS
2490+
# endif
2491+
# endif
2492+
# ifndef MAP_FAILED
2493+
# define MAP_FAILED ((void*)-1)
2494+
# endif
2495+
# endif
2496+
2497+
# if defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE)
2498+
static int accel_remap_huge_pages(void *start, size_t size, const char *name, size_t offset)
2499+
{
2500+
void *ret = MAP_FAILED;
2501+
void *mem;
2502+
int fd;
2503+
2504+
fd = open(name, O_RDONLY);
2505+
if (fd < 0) {
2506+
return -1;
2507+
}
2508+
mem = mmap(NULL, size,
2509+
PROT_READ,
2510+
MAP_PRIVATE | MAP_FILE | MAP_POPULATE,
2511+
fd, offset);
2512+
if (mem == MAP_FAILED) {
2513+
close(fd);
2514+
return -1;
2515+
}
2516+
2517+
#ifdef MAP_HUGETLB
2518+
ret = mmap(start, size,
2519+
PROT_READ | PROT_WRITE | PROT_EXEC,
2520+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_HUGETLB,
2521+
-1, 0);
2522+
#endif
2523+
#ifdef MADV_HUGEPAGE
2524+
if (ret == MAP_FAILED) {
2525+
ret = mmap(start, size,
2526+
PROT_READ | PROT_WRITE | PROT_EXEC,
2527+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
2528+
-1, 0);
2529+
madvise(start, size, MADV_HUGEPAGE);
2530+
}
2531+
#endif
2532+
if (ret == start) {
2533+
memcpy(start, mem, size);
2534+
mprotect(start, size, PROT_READ | PROT_EXEC);
2535+
}
2536+
munmap(mem, size);
2537+
close(fd);
2538+
2539+
return (ret == start) ? 0 : -1;
2540+
}
2541+
2542+
static void accel_move_code_to_huge_pages(void)
2543+
{
2544+
FILE *f;
2545+
long unsigned int huge_page_size = 2 * 1024 * 1024;
2546+
2547+
f = fopen("/proc/self/maps", "r");
2548+
if (f) {
2549+
long unsigned int start, end, offset, inode;
2550+
char perm[5], dev[6], name[MAXPATHLEN];
2551+
int ret;
2552+
2553+
ret = fscanf(f, "%lx-%lx %4s %lx %5s %ld %s\n", &start, &end, perm, &offset, dev, &inode, name);
2554+
if (ret == 7 && perm[0] == 'r' && perm[1] == '-' && perm[2] == 'x' && name[0] == '/') {
2555+
long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size);
2556+
long unsigned int seg_end = (end & ~(huge_page_size-1L));
2557+
2558+
if (seg_end > seg_start) {
2559+
zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, name);
2560+
accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, name, offset + seg_start - start);
2561+
}
2562+
}
2563+
fclose(f);
2564+
}
2565+
}
2566+
# endif
2567+
#endif /* HAVE_HUGE_CODE_PAGES */
2568+
24832569
static int accel_startup(zend_extension *extension)
24842570
{
24852571
zend_function *func;
@@ -2505,6 +2591,16 @@ static int accel_startup(zend_extension *extension)
25052591
accel_gen_system_id();
25062592
#endif
25072593

2594+
#ifdef HAVE_HUGE_CODE_PAGES
2595+
if (ZCG(accel_directives).enable_huge_code_pages &&
2596+
(strcmp(sapi_module.name, "cli") == 0 ||
2597+
strcmp(sapi_module.name, "cli-server") == 0 ||
2598+
strcmp(sapi_module.name, "cgi-fcgi") == 0 ||
2599+
strcmp(sapi_module.name, "fpm-fcgi") == 0)) {
2600+
accel_move_code_to_huge_pages();
2601+
}
2602+
#endif
2603+
25082604
/* no supported SAPI found - disable acceleration and stop initialization */
25092605
if (accel_find_sapi() == FAILURE) {
25102606
accel_startup_ok = 0;

ext/opcache/ZendAccelerator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,9 @@ typedef struct _zend_accel_directives {
219219
zend_bool file_cache_only;
220220
zend_bool file_cache_consistency_checks;
221221
#endif
222+
#ifdef HAVE_HUGE_CODE_PAGES
223+
zend_bool enable_huge_code_pages;
224+
#endif
222225
} zend_accel_directives;
223226

224227
typedef struct _zend_accel_globals {

ext/opcache/config.m4

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,19 @@ PHP_ARG_ENABLE(opcache, whether to enable Zend OPcache support,
88
PHP_ARG_ENABLE(opcache-file, whether to enable file based caching (experimental),
99
[ --enable-opcache-file Enable file based caching], no)
1010

11+
PHP_ARG_ENABLE(huge-code-pages, whether to enable copying PHP CODE pages into HUGE PAGES (experimental),
12+
[ --enable-huge-code-pages Enable copying PHP CODE pages into HUGE PAGES], no)
13+
1114
if test "$PHP_OPCACHE" != "no"; then
1215

1316
if test "$PHP_OPCACHE_FILE" == "yes"; then
1417
AC_DEFINE(HAVE_OPCACHE_FILE_CACHE, 1, [Define to enable file based caching (experimental)])
1518
fi
1619

20+
if test "$PHP_HUGE_CODE_PAGES" == "yes"; then
21+
AC_DEFINE(HAVE_HUGE_CODE_PAGES, 1, [Define to enable copying PHP CODE pages into HUGE PAGES (experimental)])
22+
fi
23+
1724
AC_CHECK_FUNC(mprotect,[
1825
AC_DEFINE(HAVE_MPROTECT, 1, [Define if you have mprotect() function])
1926
])

ext/opcache/zend_accelerator_module.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ ZEND_INI_BEGIN()
309309
STD_PHP_INI_ENTRY("opcache.file_cache_only" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_only, zend_accel_globals, accel_globals)
310310
STD_PHP_INI_ENTRY("opcache.file_cache_consistency_checks" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_consistency_checks, zend_accel_globals, accel_globals)
311311
#endif
312+
#ifdef HAVE_HUGE_CODE_PAGES
313+
STD_PHP_INI_BOOLEAN("opcache.enable_huge_code_pages" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.enable_huge_code_pages, zend_accel_globals, accel_globals)
314+
#endif
312315
ZEND_INI_END()
313316

314317
static int filename_is_in_cache(zend_string *filename)

0 commit comments

Comments
 (0)