Skip to content

Commit 9bbc195

Browse files
authored
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.
1 parent 07337df commit 9bbc195

File tree

5 files changed

+36
-47
lines changed

5 files changed

+36
-47
lines changed

Zend/zend.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,10 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
823823
executor_globals->pid = 0;
824824
executor_globals->oldact = (struct sigaction){0};
825825
#endif
826+
memset(executor_globals->strtod_state.freelist, 0,
827+
sizeof(executor_globals->strtod_state.freelist));
828+
executor_globals->strtod_state.p5s = NULL;
829+
executor_globals->strtod_state.result = NULL;
826830
}
827831
/* }}} */
828832

@@ -918,7 +922,6 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
918922
#endif
919923

920924
zend_startup_hrtime();
921-
zend_startup_strtod();
922925
zend_startup_extensions_mechanism();
923926

924927
/* Set up utility functions and values */

Zend/zend_globals.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "zend_arena.h"
4242
#include "zend_call_stack.h"
4343
#include "zend_max_execution_timer.h"
44+
#include "zend_strtod.h"
4445

4546
/* Define ZTS if you want a thread-safe Zend */
4647
/*#undef ZTS*/
@@ -304,6 +305,8 @@ struct _zend_executor_globals {
304305
struct sigaction oldact;
305306
#endif
306307

308+
zend_strtod_state strtod_state;
309+
307310
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
308311
};
309312

Zend/zend_strtod.c

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@
189189
#include <zend_operators.h>
190190
#include <zend_strtod.h>
191191
#include "zend_strtod_int.h"
192+
#include "zend_globals.h"
192193

193194
#ifndef Long
194195
#define Long int32_t
@@ -197,6 +198,16 @@
197198
#define ULong uint32_t
198199
#endif
199200

201+
#undef Bigint
202+
#undef freelist
203+
#undef p5s
204+
#undef dtoa_result
205+
206+
#define Bigint _zend_strtod_bigint
207+
#define freelist (EG(strtod_state).freelist)
208+
#define p5s (EG(strtod_state).p5s)
209+
#define dtoa_result (EG(strtod_state).result)
210+
200211
#ifdef DEBUG
201212
static void Bug(const char *message) {
202213
fprintf(stderr, "%s\n", message);
@@ -224,6 +235,7 @@ extern void *MALLOC(size_t);
224235
#endif
225236
#else
226237
#define MALLOC malloc
238+
#define FREE free
227239
#endif
228240

229241
#ifndef Omit_Private_Memory
@@ -522,7 +534,7 @@ BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflc
522534
#define FREE_DTOA_LOCK(n) /*nothing*/
523535
#endif
524536

525-
#define Kmax 7
537+
#define Kmax ZEND_STRTOD_K_MAX
526538

527539
struct
528540
Bigint {
@@ -533,37 +545,23 @@ Bigint {
533545

534546
typedef struct Bigint Bigint;
535547

548+
#ifndef Bigint
536549
static Bigint *freelist[Kmax+1];
550+
#endif
537551

538552
static void destroy_freelist(void);
539553
static void free_p5s(void);
540554

541-
#ifdef ZTS
555+
#ifdef MULTIPLE_THREADS
542556
static MUTEX_T dtoa_mutex;
543557
static MUTEX_T pow5mult_mutex;
544558
#endif /* ZTS */
545559

546-
ZEND_API int zend_startup_strtod(void) /* {{{ */
547-
{
548-
#ifdef ZTS
549-
dtoa_mutex = tsrm_mutex_alloc();
550-
pow5mult_mutex = tsrm_mutex_alloc();
551-
#endif
552-
return 1;
553-
}
554-
/* }}} */
555560
ZEND_API int zend_shutdown_strtod(void) /* {{{ */
556561
{
557562
destroy_freelist();
558563
free_p5s();
559564

560-
#ifdef ZTS
561-
tsrm_mutex_free(dtoa_mutex);
562-
dtoa_mutex = NULL;
563-
564-
tsrm_mutex_free(pow5mult_mutex);
565-
pow5mult_mutex = NULL;
566-
#endif
567565
return 1;
568566
}
569567
/* }}} */
@@ -627,11 +625,7 @@ Bfree
627625
{
628626
if (v) {
629627
if (v->k > Kmax)
630-
#ifdef FREE
631628
FREE((void*)v);
632-
#else
633-
free((void*)v);
634-
#endif
635629
else {
636630
ACQUIRE_DTOA_LOCK(0);
637631
v->next = freelist[v->k];
@@ -947,7 +941,9 @@ mult
947941
return c;
948942
}
949943

944+
#ifndef p5s
950945
static Bigint *p5s;
946+
#endif
951947

952948
static Bigint *
953949
pow5mult
@@ -3602,7 +3598,7 @@ zend_strtod
36023598
return sign ? -dval(&rv) : dval(&rv);
36033599
}
36043600

3605-
#ifndef MULTIPLE_THREADS
3601+
#if !defined(MULTIPLE_THREADS) && !defined(dtoa_result)
36063602
ZEND_TLS char *dtoa_result;
36073603
#endif
36083604

@@ -4616,7 +4612,7 @@ static void destroy_freelist(void)
46164612
Bigint **listp = &freelist[i];
46174613
while ((tmp = *listp) != NULL) {
46184614
*listp = tmp->next;
4619-
free(tmp);
4615+
FREE(tmp);
46204616
}
46214617
freelist[i] = NULL;
46224618
}
@@ -4631,7 +4627,8 @@ static void free_p5s(void)
46314627
listp = &p5s;
46324628
while ((tmp = *listp) != NULL) {
46334629
*listp = tmp->next;
4634-
free(tmp);
4630+
FREE(tmp);
46354631
}
4632+
p5s = NULL;
46364633
FREE_DTOA_LOCK(1)
46374634
}

Zend/zend_strtod.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,20 @@
2323
#include <zend.h>
2424

2525
BEGIN_EXTERN_C()
26+
#define ZEND_STRTOD_K_MAX 7
27+
typedef struct _zend_strtod_bigint zend_strtod_bigint;
28+
typedef struct _zend_strtod_state {
29+
zend_strtod_bigint *freelist[ZEND_STRTOD_K_MAX+1];
30+
zend_strtod_bigint *p5s;
31+
char *result;
32+
} zend_strtod_state;
2633
ZEND_API void zend_freedtoa(char *s);
2734
ZEND_API char *zend_dtoa(double _d, int mode, int ndigits, int *decpt, bool *sign, char **rve);
2835
ZEND_API char *zend_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf);
2936
ZEND_API double zend_strtod(const char *s00, const char **se);
3037
ZEND_API double zend_hex_strtod(const char *str, const char **endptr);
3138
ZEND_API double zend_oct_strtod(const char *str, const char **endptr);
3239
ZEND_API double zend_bin_strtod(const char *str, const char **endptr);
33-
ZEND_API int zend_startup_strtod(void);
3440
ZEND_API int zend_shutdown_strtod(void);
3541
END_EXTERN_C()
3642

Zend/zend_strtod_int.h

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -104,24 +104,4 @@
104104
#endif
105105
#endif
106106

107-
#ifdef ZTS
108-
#define MULTIPLE_THREADS 1
109-
110-
#define ACQUIRE_DTOA_LOCK(x) \
111-
if (0 == x) { \
112-
tsrm_mutex_lock(dtoa_mutex); \
113-
} else if (1 == x) { \
114-
tsrm_mutex_lock(pow5mult_mutex); \
115-
}
116-
117-
#define FREE_DTOA_LOCK(x) \
118-
if (0 == x) { \
119-
tsrm_mutex_unlock(dtoa_mutex); \
120-
} else if (1 == x) { \
121-
tsrm_mutex_unlock(pow5mult_mutex); \
122-
}
123-
124-
125-
#endif
126-
127107
#endif /* ZEND_STRTOD_INT_H */

0 commit comments

Comments
 (0)