Remove zend_strtod mutex (#13974)

`zend_strtod.c` uses a global state (mostly an allocation freelist) protected by a mutex in ZTS builds. This state is used by `zend_dtoa()`, `zend_strtod()`, and variants. This creates a lot of contention in concurrent loads. `zend_dtoa()` is used to format floats to string, e.g. in sprintf, json_encode, serialize, uniqid.

Here I move the global state to the thread specific `executor_globals` and remove the mutex.

The impact on non-concurrent environments is null or negligible, but there is a considerable speed up on concurrent environments, especially on Alpine/Musl.
This commit is contained in:
Arnaud Le Blanc 2024-04-23 11:52:38 +02:00 committed by GitHub
parent 07337df1d7
commit 9bbc195d11
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 36 additions and 47 deletions

View File

@ -823,6 +823,10 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
executor_globals->pid = 0;
executor_globals->oldact = (struct sigaction){0};
#endif
memset(executor_globals->strtod_state.freelist, 0,
sizeof(executor_globals->strtod_state.freelist));
executor_globals->strtod_state.p5s = NULL;
executor_globals->strtod_state.result = NULL;
}
/* }}} */
@ -918,7 +922,6 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
#endif
zend_startup_hrtime();
zend_startup_strtod();
zend_startup_extensions_mechanism();
/* Set up utility functions and values */

View File

@ -41,6 +41,7 @@
#include "zend_arena.h"
#include "zend_call_stack.h"
#include "zend_max_execution_timer.h"
#include "zend_strtod.h"
/* Define ZTS if you want a thread-safe Zend */
/*#undef ZTS*/
@ -304,6 +305,8 @@ struct _zend_executor_globals {
struct sigaction oldact;
#endif
zend_strtod_state strtod_state;
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};

View File

@ -189,6 +189,7 @@
#include <zend_operators.h>
#include <zend_strtod.h>
#include "zend_strtod_int.h"
#include "zend_globals.h"
#ifndef Long
#define Long int32_t
@ -197,6 +198,16 @@
#define ULong uint32_t
#endif
#undef Bigint
#undef freelist
#undef p5s
#undef dtoa_result
#define Bigint _zend_strtod_bigint
#define freelist (EG(strtod_state).freelist)
#define p5s (EG(strtod_state).p5s)
#define dtoa_result (EG(strtod_state).result)
#ifdef DEBUG
static void Bug(const char *message) {
fprintf(stderr, "%s\n", message);
@ -224,6 +235,7 @@ extern void *MALLOC(size_t);
#endif
#else
#define MALLOC malloc
#define FREE free
#endif
#ifndef Omit_Private_Memory
@ -522,7 +534,7 @@ BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflc
#define FREE_DTOA_LOCK(n) /*nothing*/
#endif
#define Kmax 7
#define Kmax ZEND_STRTOD_K_MAX
struct
Bigint {
@ -533,37 +545,23 @@ Bigint {
typedef struct Bigint Bigint;
#ifndef Bigint
static Bigint *freelist[Kmax+1];
#endif
static void destroy_freelist(void);
static void free_p5s(void);
#ifdef ZTS
#ifdef MULTIPLE_THREADS
static MUTEX_T dtoa_mutex;
static MUTEX_T pow5mult_mutex;
#endif /* ZTS */
ZEND_API int zend_startup_strtod(void) /* {{{ */
{
#ifdef ZTS
dtoa_mutex = tsrm_mutex_alloc();
pow5mult_mutex = tsrm_mutex_alloc();
#endif
return 1;
}
/* }}} */
ZEND_API int zend_shutdown_strtod(void) /* {{{ */
{
destroy_freelist();
free_p5s();
#ifdef ZTS
tsrm_mutex_free(dtoa_mutex);
dtoa_mutex = NULL;
tsrm_mutex_free(pow5mult_mutex);
pow5mult_mutex = NULL;
#endif
return 1;
}
/* }}} */
@ -627,11 +625,7 @@ Bfree
{
if (v) {
if (v->k > Kmax)
#ifdef FREE
FREE((void*)v);
#else
free((void*)v);
#endif
else {
ACQUIRE_DTOA_LOCK(0);
v->next = freelist[v->k];
@ -947,7 +941,9 @@ mult
return c;
}
#ifndef p5s
static Bigint *p5s;
#endif
static Bigint *
pow5mult
@ -3602,7 +3598,7 @@ zend_strtod
return sign ? -dval(&rv) : dval(&rv);
}
#ifndef MULTIPLE_THREADS
#if !defined(MULTIPLE_THREADS) && !defined(dtoa_result)
ZEND_TLS char *dtoa_result;
#endif
@ -4616,7 +4612,7 @@ static void destroy_freelist(void)
Bigint **listp = &freelist[i];
while ((tmp = *listp) != NULL) {
*listp = tmp->next;
free(tmp);
FREE(tmp);
}
freelist[i] = NULL;
}
@ -4631,7 +4627,8 @@ static void free_p5s(void)
listp = &p5s;
while ((tmp = *listp) != NULL) {
*listp = tmp->next;
free(tmp);
FREE(tmp);
}
p5s = NULL;
FREE_DTOA_LOCK(1)
}

View File

@ -23,6 +23,13 @@
#include <zend.h>
BEGIN_EXTERN_C()
#define ZEND_STRTOD_K_MAX 7
typedef struct _zend_strtod_bigint zend_strtod_bigint;
typedef struct _zend_strtod_state {
zend_strtod_bigint *freelist[ZEND_STRTOD_K_MAX+1];
zend_strtod_bigint *p5s;
char *result;
} zend_strtod_state;
ZEND_API void zend_freedtoa(char *s);
ZEND_API char *zend_dtoa(double _d, int mode, int ndigits, int *decpt, bool *sign, char **rve);
ZEND_API char *zend_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf);
@ -30,7 +37,6 @@ ZEND_API double zend_strtod(const char *s00, const char **se);
ZEND_API double zend_hex_strtod(const char *str, const char **endptr);
ZEND_API double zend_oct_strtod(const char *str, const char **endptr);
ZEND_API double zend_bin_strtod(const char *str, const char **endptr);
ZEND_API int zend_startup_strtod(void);
ZEND_API int zend_shutdown_strtod(void);
END_EXTERN_C()

View File

@ -104,24 +104,4 @@
#endif
#endif
#ifdef ZTS
#define MULTIPLE_THREADS 1
#define ACQUIRE_DTOA_LOCK(x) \
if (0 == x) { \
tsrm_mutex_lock(dtoa_mutex); \
} else if (1 == x) { \
tsrm_mutex_lock(pow5mult_mutex); \
}
#define FREE_DTOA_LOCK(x) \
if (0 == x) { \
tsrm_mutex_unlock(dtoa_mutex); \
} else if (1 == x) { \
tsrm_mutex_unlock(pow5mult_mutex); \
}
#endif
#endif /* ZEND_STRTOD_INT_H */