From: Dmitry Stogov Date: Wed, 18 Oct 2017 21:02:56 +0000 (+0300) Subject: Extend zend_string API to avoid reallocation in zend_string_init_interned() X-Git-Tag: php-7.3.0alpha1~1245 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=272f5a1716155f21ccce8dc625aeeabbcbe6bdaa;p=php Extend zend_string API to avoid reallocation in zend_string_init_interned() --- diff --git a/Zend/zend_API.c b/Zend/zend_API.c index b9467da710..fd1705149b 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2177,7 +2177,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio while (ptr->fname) { fname_len = strlen(ptr->fname); internal_function->handler = ptr->handler; - internal_function->function_name = zend_new_interned_string(zend_string_init(ptr->fname, fname_len, 1)); + internal_function->function_name = zend_string_init_interned(ptr->fname, fname_len, 1); internal_function->scope = scope; internal_function->prototype = NULL; if (ptr->flags) { @@ -2307,7 +2307,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio class_name++; allow_null = 1; } - str = zend_new_interned_string(zend_string_init(class_name, strlen(class_name), 1)); + str = zend_string_init_interned(class_name, strlen(class_name), 1); new_arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(str, allow_null); } } @@ -3863,9 +3863,12 @@ ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, { int ret; - zend_string *key = zend_string_init(name, name_length, ce->type & ZEND_INTERNAL_CLASS); + zend_string *key; + if (ce->type == ZEND_INTERNAL_CLASS) { - key = zend_new_interned_string(key); + key = zend_string_init_interned(name, name_length, 1); + } else { + key = zend_string_init(name, name_length, 0); } ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL); zend_string_release(key); diff --git a/Zend/zend_API.h b/Zend/zend_API.h index c3220d2685..df9e136e4a 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -193,9 +193,7 @@ typedef struct _zend_fcall_info_cache { #define INIT_OVERLOADED_CLASS_ENTRY_EX(class_container, class_name, class_name_len, functions, handle_fcall, handle_propget, handle_propset, handle_propunset, handle_propisset) \ { \ - zend_string *cl_name; \ - cl_name = zend_string_init(class_name, class_name_len, 1); \ - class_container.name = zend_new_interned_string(cl_name); \ + class_container.name = zend_string_init_interned(class_name, class_name_len, 1); \ INIT_CLASS_ENTRY_INIT_METHODS(class_container, functions, handle_fcall, handle_propget, handle_propset, handle_propunset, handle_propisset) \ } diff --git a/Zend/zend_string.c b/Zend/zend_string.c index 4cfd3dedec..ab8fc7f407 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -22,9 +22,12 @@ #include "zend_globals.h" ZEND_API zend_string *(*zend_new_interned_string)(zend_string *str); +ZEND_API zend_string *(*zend_string_init_interned)(const char *str, size_t size, int permanent); static zend_string *zend_new_interned_string_permanent(zend_string *str); static zend_string *zend_new_interned_string_request(zend_string *str); +static zend_string *zend_string_init_interned_permanent(const char *str, size_t size, int permanent); +static zend_string *zend_string_init_interned_request(const char *str, size_t size, int permanent); /* Any strings interned in the startup phase. Common to all the threads, won't be free'd until process exit. If we want an ability to @@ -33,6 +36,7 @@ static zend_string *zend_new_interned_string_request(zend_string *str); static HashTable interned_strings_permanent; static zend_new_interned_string_func_t interned_string_request_handler = zend_new_interned_string_request; +static zend_string_init_interned_func_t interned_string_init_request_handler = zend_string_init_interned_request; static zend_string_copy_storage_func_t interned_string_copy_storage = NULL; ZEND_API zend_string *zend_empty_string = NULL; @@ -72,6 +76,7 @@ ZEND_API void zend_interned_strings_init(void) zend_init_interned_strings_ht(&interned_strings_permanent, 1); zend_new_interned_string = zend_new_interned_string_permanent; + zend_string_init_interned = zend_string_init_interned_permanent; /* interned empty string */ str = zend_string_alloc(sizeof("")-1, 1); @@ -100,20 +105,18 @@ ZEND_API void zend_interned_strings_dtor(void) zend_known_strings = NULL; } -static zend_always_inline zend_string *zend_interned_string_ht_lookup(zend_string *str, HashTable *interned_strings) +static zend_always_inline zend_string *zend_interned_string_ht_lookup_ex(zend_ulong h, const char *str, size_t size, HashTable *interned_strings) { - zend_ulong h; uint32_t nIndex; uint32_t idx; Bucket *p; - h = zend_string_hash_val(str); nIndex = h | interned_strings->nTableMask; idx = HT_HASH(interned_strings, nIndex); while (idx != HT_INVALID_IDX) { p = HT_HASH_TO_BUCKET(interned_strings, idx); - if ((p->h == h) && (ZSTR_LEN(p->key) == ZSTR_LEN(str))) { - if (!memcmp(ZSTR_VAL(p->key), ZSTR_VAL(str), ZSTR_LEN(str))) { + if ((p->h == h) && (ZSTR_LEN(p->key) == size)) { + if (!memcmp(ZSTR_VAL(p->key), str, size)) { return p->key; } } @@ -123,6 +126,11 @@ static zend_always_inline zend_string *zend_interned_string_ht_lookup(zend_strin return NULL; } +static zend_always_inline zend_string *zend_interned_string_ht_lookup(zend_string *str, HashTable *interned_strings) +{ + return zend_interned_string_ht_lookup_ex(ZSTR_H(str), ZSTR_VAL(str), ZSTR_LEN(str), interned_strings); +} + /* This function might be not thread safe at least because it would update the hash val in the passed string. Be sure it is called in the appropriate context. */ static zend_always_inline zend_string *zend_add_interned_string(zend_string *str, HashTable *interned_strings, uint32_t flags) @@ -141,10 +149,10 @@ static zend_always_inline zend_string *zend_add_interned_string(zend_string *str ZEND_API zend_string *zend_interned_string_find_permanent(zend_string *str) { + zend_string_hash_val(str); return zend_interned_string_ht_lookup(str, &interned_strings_permanent); } - static zend_string *zend_new_interned_string_permanent(zend_string *str) { zend_string *ret; @@ -153,6 +161,7 @@ static zend_string *zend_new_interned_string_permanent(zend_string *str) return str; } + zend_string_hash_val(str); ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent); if (ret) { zend_string_release(str); @@ -170,6 +179,8 @@ static zend_string *zend_new_interned_string_request(zend_string *str) return str; } + zend_string_hash_val(str); + /* Check for permanent strings, the table is readonly at this point. */ ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent); if (ret) { @@ -189,6 +200,44 @@ static zend_string *zend_new_interned_string_request(zend_string *str) return ret; } +static zend_string *zend_string_init_interned_permanent(const char *str, size_t size, int permanent) +{ + zend_string *ret; + zend_long h = zend_inline_hash_func(str, size); + + ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent); + if (ret) { + return ret; + } + + ret = zend_string_init(str, size, permanent); + ZSTR_H(ret) = h; + return zend_add_interned_string(ret, &interned_strings_permanent, IS_STR_PERMANENT); +} + +static zend_string *zend_string_init_interned_request(const char *str, size_t size, int permanent) +{ + zend_string *ret; + zend_long h = zend_inline_hash_func(str, size); + + /* Check for permanent strings, the table is readonly at this point. */ + ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent); + if (ret) { + return ret; + } + + ret = zend_interned_string_ht_lookup_ex(h, str, size, &CG(interned_strings)); + if (ret) { + return ret; + } + + ret = zend_string_init(str, size, permanent); + ZSTR_H(ret) = h; + + /* Create a short living interned, freed after the request. */ + return zend_add_interned_string(ret, &CG(interned_strings), 0); +} + ZEND_API void zend_interned_strings_activate(void) { zend_init_interned_strings_ht(&CG(interned_strings), 0); @@ -199,9 +248,10 @@ ZEND_API void zend_interned_strings_deactivate(void) zend_hash_destroy(&CG(interned_strings)); } -ZEND_API void zend_interned_strings_set_request_storage_handler(zend_new_interned_string_func_t handler) +ZEND_API void zend_interned_strings_set_request_storage_handlers(zend_new_interned_string_func_t handler, zend_string_init_interned_func_t init_handler) { interned_string_request_handler = handler; + interned_string_init_request_handler = init_handler; } ZEND_API void zend_interned_strings_set_permanent_storage_copy_handler(zend_string_copy_storage_func_t handler) @@ -215,6 +265,7 @@ ZEND_API void zend_interned_strings_switch_storage(void) interned_string_copy_storage(); } zend_new_interned_string = interned_string_request_handler; + zend_string_init_interned = interned_string_init_request_handler; } /* diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 400795a9b2..7f5b108383 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -27,8 +27,10 @@ BEGIN_EXTERN_C() typedef void (*zend_string_copy_storage_func_t)(void); typedef zend_string *(*zend_new_interned_string_func_t)(zend_string *str); +typedef zend_string *(*zend_string_init_interned_func_t)(const char *str, size_t size, int permanent); ZEND_API extern zend_new_interned_string_func_t zend_new_interned_string; +ZEND_API extern zend_string_init_interned_func_t zend_string_init_interned; ZEND_API zend_ulong zend_hash_func(const char *str, size_t len); ZEND_API void zend_interned_strings_init(void); @@ -36,7 +38,7 @@ ZEND_API void zend_interned_strings_dtor(void); ZEND_API void zend_interned_strings_activate(void); ZEND_API void zend_interned_strings_deactivate(void); ZEND_API zend_string *zend_interned_string_find_permanent(zend_string *str); -ZEND_API void zend_interned_strings_set_request_storage_handler(zend_new_interned_string_func_t handler); +ZEND_API void zend_interned_strings_set_request_storage_handlers(zend_new_interned_string_func_t handler, zend_string_init_interned_func_t init_handler); ZEND_API void zend_interned_strings_set_permanent_storage_copy_handler(zend_string_copy_storage_func_t handler); ZEND_API void zend_interned_strings_switch_storage(void); @@ -174,13 +176,6 @@ static zend_always_inline zend_string *zend_string_init(const char *str, size_t return ret; } -static zend_always_inline zend_string *zend_string_init_interned(const char *str, size_t len, int persistent) -{ - zend_string *ret = zend_string_init(str, len, persistent); - - return zend_new_interned_string(ret); -} - static zend_always_inline zend_string *zend_string_copy(zend_string *s) { if (!ZSTR_IS_INTERNED(s)) { diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index b948b8b81c..ed6931ef41 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -1375,7 +1375,7 @@ php_mysqlnd_rset_field_read(void * _packet) meta->root_len = total_len; if (meta->name != mysqlnd_empty_string) { - meta->sname = zend_new_interned_string(zend_string_init(meta->name, meta->name_length, packet->persistent_alloc)); + meta->sname = zend_string_init_interned(meta->name, meta->name_length, packet->persistent_alloc); } else { meta->sname = ZSTR_EMPTY_ALLOC(); } diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index cf1e1ce7d3..d0edfa08a2 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -502,6 +502,47 @@ static zend_string *accel_new_interned_string_for_php(zend_string *str) return str; } +static zend_string *accel_find_interned_string_ex(zend_ulong h, const char *str, size_t size) +{ + uint32_t nIndex; + uint32_t idx; + Bucket *arData, *p; + + nIndex = h | ZCSG(interned_strings).nTableMask; + + /* check for existing interned string */ + idx = HT_HASH(&ZCSG(interned_strings), nIndex); + arData = ZCSG(interned_strings).arData; + while (idx != HT_INVALID_IDX) { + p = HT_HASH_TO_BUCKET_EX(arData, idx); + if ((p->h == h) && (ZSTR_LEN(p->key) == size)) { + if (!memcmp(ZSTR_VAL(p->key), str, size)) { + return p->key; + } + } + idx = Z_NEXT(p->val); + } + + return NULL; +} + +static zend_string *accel_init_interned_string_for_php(const char *str, size_t size, int permanent) +{ + if (ZCG(counted)) { + zend_ulong h = zend_inline_hash_func(str, size); + zend_string *ret = accel_find_interned_string_ex(h, str, size); + + if (!ret) { + ret = zend_string_init(str, size, permanent); + ZSTR_H(ret) = h; + } + + return ret; + } + + return zend_string_init(str, size, permanent); +} + /* Copy PHP interned strings from PHP process memory into the shared memory */ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_interned_string) { @@ -2389,7 +2430,7 @@ static int zend_accel_init_shm(void) zend_interned_strings_set_permanent_storage_copy_handler(accel_use_shm_interned_strings); } - zend_interned_strings_set_request_storage_handler(accel_new_interned_string_for_php); + zend_interned_strings_set_request_storage_handlers(accel_new_interned_string_for_php, accel_init_interned_string_for_php); zend_reset_cache_vars(); @@ -2658,7 +2699,7 @@ static int accel_post_startup(void) if (ZCG(accel_directives).interned_strings_buffer) { zend_interned_strings_set_permanent_storage_copy_handler(accel_use_shm_interned_strings); } - zend_interned_strings_set_request_storage_handler(accel_new_interned_string_for_php); + zend_interned_strings_set_request_storage_handlers(accel_new_interned_string_for_php, accel_init_interned_string_for_php); zend_shared_alloc_unlock(); break; case FAILED_REATTACHED: