Re: com php-src: 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.:
| From: | Xinchen Hui | Date: | Mon, 14 Sep 2015 12:35:48 +0000 |
| Subject: | Re: com php-src: 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.: |
||
| References: | 1 2 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-88182@lists.php.net to get a copy of this message | ||
Hey:
On Mon, Sep 14, 2015 at 6:57 PM, Dmitry Stogov <dmitry@zend.com> wrote:
> The effect of this patch is really exiting to keep it disabled by default...
> :)
>
> $ perf stat -e instructions,iTLB-loads,iTLB-load-misses,cycles
> sapi/cgi/php-cgi -d opcache.enable_huge_code_pages=0 -T 10000
> /var/www/html/bench/wordpress-3.6/index.php > /dev/null
>
> 191,778,620,621 instructions # 0.81 insns per cycle
> 178,497,016 iTLB-loads
> 87,351,276 iTLB-load-misses # 48.94% of all iTLB
> cache hits
> 238,136,402,797 cycles
>
> 110.221114219 seconds time elapsed
>
> $ perf stat -e instructions,iTLB-loads,iTLB-load-misses,cycles
> sapi/cgi/php-cgi -d opcache.enable_huge_code_pages=1 -T 10000
> /var/www/html/bench/wordpress-3.6/index.php > /dev/null
>
> 191,780,181,536 instructions # 0.83 insns per cycle
> 39,017,004 iTLB-loads
> 41,631,359 iTLB-load-misses # 106.70% of all iTLB
> cache hits
> 231,287,491,307 cycles
>
> 107.742837269 seconds time elapsed
>
> May be we should enable this by default in RC (may be also file cache) and
> make decision based on feedback?
>
For me, it is a safe improvement, and it shouldn't break any
functions (except the perf report/anno symbols lost, which actually is
a limitation by current perf tool self , maybe it could be fixed
later).
and it give us a significant improvement, I'd like to see it to be
enabled by default.
thanks
> Thanks. Dmtiry.
>
>
>
> On Mon, Sep 14, 2015 at 1:34 PM, Dmitry Stogov <dmitry@php.net> wrote:
>>
>> Commit: 669f0b39b184593e01e677360fd79b2b63058ca0
>> Author: Dmitry Stogov <dmitry@zend.com> Mon, 14 Sep 2015
>> 13:34:17 +0300
>> Parents: 145708b658d0a60b738dde1a942dea9032f2f54e
>> Branches: master
>>
>> Link:
>>
>> http://git.php.net/?p=php-src.git;a=commitdiff;h=669f0b39b184593e01e677360fd79b2b63058ca0
>>
>> Log:
>> 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.
>>
>> Changed paths:
>> M ext/opcache/ZendAccelerator.c
>> M ext/opcache/ZendAccelerator.h
>> M ext/opcache/config.m4
>> M ext/opcache/zend_accelerator_module.c
>>
>>
>> Diff:
>> diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
>> index 905700e..2c3d9a4 100644
>> --- a/ext/opcache/ZendAccelerator.c
>> +++ b/ext/opcache/ZendAccelerator.c
>> @@ -2480,6 +2480,92 @@ static void accel_gen_system_id(void)
>> }
>> #endif
>>
>> +#ifdef HAVE_HUGE_CODE_PAGES
>> +# ifndef _WIN32
>> +# include <sys/mman.h>
>> +# include <sys/prctl.h>
>> +# ifndef MAP_ANON
>> +# ifdef MAP_ANONYMOUS
>> +# define MAP_ANON MAP_ANONYMOUS
>> +# endif
>> +# endif
>> +# ifndef MAP_FAILED
>> +# define MAP_FAILED ((void*)-1)
>> +# endif
>> +# endif
>> +
>> +# if defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE)
>> +static int accel_remap_huge_pages(void *start, size_t size, const char
>> *name, size_t offset)
>> +{
>> + void *ret = MAP_FAILED;
>> + void *mem;
>> + int fd;
>> +
>> + fd = open(name, O_RDONLY);
>> + if (fd < 0) {
>> + return -1;
>> + }
>> + mem = mmap(NULL, size,
>> + PROT_READ,
>> + MAP_PRIVATE | MAP_FILE | MAP_POPULATE,
>> + fd, offset);
>> + if (mem == MAP_FAILED) {
>> + close(fd);
>> + return -1;
>> + }
>> +
>> +#ifdef MAP_HUGETLB
>> + ret = mmap(start, size,
>> + PROT_READ | PROT_WRITE | PROT_EXEC,
>> + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_HUGETLB,
>> + -1, 0);
>> +#endif
>> +#ifdef MADV_HUGEPAGE
>> + if (ret == MAP_FAILED) {
>> + ret = mmap(start, size,
>> + PROT_READ | PROT_WRITE | PROT_EXEC,
>> + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
>> + -1, 0);
>> + madvise(start, size, MADV_HUGEPAGE);
>> + }
>> +#endif
>> + if (ret == start) {
>> + memcpy(start, mem, size);
>> + mprotect(start, size, PROT_READ | PROT_EXEC);
>> + }
>> + munmap(mem, size);
>> + close(fd);
>> +
>> + return (ret == start) ? 0 : -1;
>> +}
>> +
>> +static void accel_move_code_to_huge_pages(void)
>> +{
>> + FILE *f;
>> + long unsigned int huge_page_size = 2 * 1024 * 1024;
>> +
>> + f = fopen("/proc/self/maps", "r");
>> + if (f) {
>> + long unsigned int start, end, offset, inode;
>> + char perm[5], dev[6], name[MAXPATHLEN];
>> + int ret;
>> +
>> + ret = fscanf(f, "%lx-%lx %4s %lx %5s %ld %s\n", &start,
>> &end, perm, &offset, dev, &inode, name);
>> + if (ret == 7 && perm[0] == 'r' && perm[1] ==
>> '-' &&
>> perm[2] == 'x' && name[0] == '/') {
>> + long unsigned int seg_start =
>> ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size);
>> + long unsigned int seg_end = (end &
>> ~(huge_page_size-1L));
>> +
>> + if (seg_end > seg_start) {
>> + zend_accel_error(ACCEL_LOG_DEBUG, "remap
>> to huge page %lx-%lx %s \n", seg_start, seg_end, name);
>> + accel_remap_huge_pages((void*)seg_start,
>> seg_end - seg_start, name, offset + seg_start - start);
>> + }
>> + }
>> + fclose(f);
>> + }
>> +}
>> +# endif
>> +#endif /* HAVE_HUGE_CODE_PAGES */
>> +
>> static int accel_startup(zend_extension *extension)
>> {
>> zend_function *func;
>> @@ -2505,6 +2591,16 @@ static int accel_startup(zend_extension *extension)
>> accel_gen_system_id();
>> #endif
>>
>> +#ifdef HAVE_HUGE_CODE_PAGES
>> + if (ZCG(accel_directives).enable_huge_code_pages &&
>> + (strcmp(sapi_module.name, "cli") == 0 ||
>> + strcmp(sapi_module.name, "cli-server") == 0 ||
>> + strcmp(sapi_module.name, "cgi-fcgi") == 0 ||
>> + strcmp(sapi_module.name, "fpm-fcgi") == 0)) {
>> + accel_move_code_to_huge_pages();
>> + }
>> +#endif
>> +
>> /* no supported SAPI found - disable acceleration and stop
>> initialization */
>> if (accel_find_sapi() == FAILURE) {
>> accel_startup_ok = 0;
>> diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h
>> index 6ce4613..da3fc66 100644
>> --- a/ext/opcache/ZendAccelerator.h
>> +++ b/ext/opcache/ZendAccelerator.h
>> @@ -219,6 +219,9 @@ typedef struct _zend_accel_directives {
>> zend_bool file_cache_only;
>> zend_bool file_cache_consistency_checks;
>> #endif
>> +#ifdef HAVE_HUGE_CODE_PAGES
>> + zend_bool enable_huge_code_pages;
>> +#endif
>> } zend_accel_directives;
>>
>> typedef struct _zend_accel_globals {
>> diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4
>> index 856f24a..d8f84e2 100644
>> --- a/ext/opcache/config.m4
>> +++ b/ext/opcache/config.m4
>> @@ -8,12 +8,19 @@ PHP_ARG_ENABLE(opcache, whether to enable Zend OPcache
>> support,
>> PHP_ARG_ENABLE(opcache-file, whether to enable file based caching
>> (experimental),
>> [ --enable-opcache-file Enable file based caching], no)
>>
>> +PHP_ARG_ENABLE(huge-code-pages, whether to enable copying PHP CODE pages
>> into HUGE PAGES (experimental),
>> +[ --enable-huge-code-pages Enable copying PHP CODE pages into HUGE
>> PAGES], no)
>> +
>> if test "$PHP_OPCACHE" != "no"; then
>>
>> if test "$PHP_OPCACHE_FILE" == "yes"; then
>> AC_DEFINE(HAVE_OPCACHE_FILE_CACHE, 1, [Define to enable file based
>> caching (experimental)])
>> fi
>>
>> + if test "$PHP_HUGE_CODE_PAGES" == "yes"; then
>> + AC_DEFINE(HAVE_HUGE_CODE_PAGES, 1, [Define to enable copying PHP CODE
>> pages into HUGE PAGES (experimental)])
>> + fi
>> +
>> AC_CHECK_FUNC(mprotect,[
>> AC_DEFINE(HAVE_MPROTECT, 1, [Define if you have mprotect() function])
>> ])
>> diff --git a/ext/opcache/zend_accelerator_module.c
>> b/ext/opcache/zend_accelerator_module.c
>> index eb93f59..5dfa8c1 100644
>> --- a/ext/opcache/zend_accelerator_module.c
>> +++ b/ext/opcache/zend_accelerator_module.c
>> @@ -309,6 +309,9 @@ ZEND_INI_BEGIN()
>> STD_PHP_INI_ENTRY("opcache.file_cache_only" , "0"
>> , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_only,
>> zend_accel_globals, accel_globals)
>> 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)
>> #endif
>> +#ifdef HAVE_HUGE_CODE_PAGES
>> + 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)
>> +#endif
>> ZEND_INI_END()
>>
>> static int filename_is_in_cache(zend_string *filename)
>>
>>
>> --
>> PHP CVS Mailing List (http://www.php.net/)
>> To unsubscribe, visit: http://www.php.net/unsub.php
>>
>
--
Xinchen Hui
@Laruence
http://www.laruence.com/