Skip to content

Commit a43cc05

Browse files
committed
ext/mysqlnd: Use relaxed atomics for statistics in ZTS builds
In ZTS builds every statistics update locked a mutex, and the global statistics are one object shared by all threads of the process. The values are now atomic_uint_least64_t updated with relaxed atomic additions, and LOCK_access is removed. NTS builds are unchanged. FrankenPHP 1.13.1 in worker mode with 16 threads, one prepared SELECT of 20 rows per request, AMD Ryzen 9 5950X, GCC 15.2. CPU time per request with statistics on (the default), by the cores FrankenPHP runs on: - 4 cores of one CCD: 0.3029 -> 0.2722 ms (-10%) - 4 cores, two on each CCD: 0.3553 -> 0.2934 ms (-17%) - 8 cores of one CCD: 0.3810 -> 0.2739 ms (-28%) - 8 cores, four on each CCD: 0.5274 -> 0.2781 ms (-47%) Statistics on now cost 0.6-1.4% over statistics off, against 12%, 23%, 41% and 91% before.
1 parent b7256ee commit a43cc05

6 files changed

Lines changed: 34 additions & 21 deletions

File tree

‎UPGRADING.INTERNALS‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ PHP 8.7 INTERNALS UPGRADE NOTES
7474
mysqlnd_reverse_api_register_api() and mysqlnd_reverse_api_get_api_list()
7575
no longer exist.
7676

77+
- Mysqlnd:
78+
. In ZTS builds the values of MYSQLND_STATS are atomic_uint_least64_t and
79+
the LOCK_access member was removed. The MYSQLND_INC_STATISTIC*() and
80+
MYSQLND_DEC_STATISTIC() macros update them with relaxed atomic additions.
81+
. The MYSQLND_STATS_LOCK() and MYSQLND_STATS_UNLOCK() macros were removed.
82+
MYSQLND_STATS_UPDATE_VALUE() is now an expression instead of a block.
83+
7784
- Standard:
7885
. php_explode() now takes a HashTable* instead of a zval* to store the
7986
resulting parts. It already expected the zval* to be IS_ARRAY.

‎ext/mysqlnd/mysqlnd_block_alloc.c‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ PHPAPI void
5353
mysqlnd_mempool_destroy(MYSQLND_MEMORY_POOL * pool)
5454
{
5555
DBG_ENTER("mysqlnd_mempool_destroy");
56-
/* mnd_free will reference LOCK_access and might crash, depending on the caller...*/
5756
zend_arena_destroy(pool->arena);
5857
DBG_VOID_RETURN;
5958
}

‎ext/mysqlnd/mysqlnd_driver.c‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ PHPAPI void mysqlnd_library_init(void)
6969
mysqlnd_conn_set_methods(&MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_conn));
7070
mysqlnd_conn_data_set_methods(&MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_conn_data));
7171
_mysqlnd_init_ps_subsystem();
72-
/* Should be calloc, as mnd_calloc will reference LOCK_access*/
7372
mysqlnd_stats_init(&mysqlnd_global_stats, STAT_LAST, 1);
7473
mysqlnd_plugin_subsystem_init();
7574
{

‎ext/mysqlnd/mysqlnd_statistics.c‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,12 @@ mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, const MYSQLND_STRING
201201
for (i = 0; i < stats->count; i++) {
202202
char tmp[25];
203203

204-
snprintf(tmp, sizeof(tmp), "%" PRIu64, stats->values[i]);
204+
#ifdef ZTS
205+
uint64_t value = atomic_load_explicit(&stats->values[i], memory_order_relaxed);
206+
#else
207+
uint64_t value = stats->values[i];
208+
#endif
209+
snprintf(tmp, sizeof(tmp), "%" PRIu64, value);
205210
add_assoc_string_ex(return_value, names[i].s, names[i].l, tmp);
206211
}
207212
}
@@ -216,7 +221,9 @@ mysqlnd_stats_init(MYSQLND_STATS ** stats, const size_t statistic_count, const b
216221
*stats = pecalloc(1, size, persistent);
217222
(*stats)->count = statistic_count;
218223
#ifdef ZTS
219-
(*stats)->LOCK_access = tsrm_mutex_alloc();
224+
for (size_t i = 0; i < statistic_count; i++) {
225+
atomic_init(&(*stats)->values[i], 0);
226+
}
220227
#endif
221228
}
222229
/* }}} */
@@ -226,10 +233,7 @@ mysqlnd_stats_init(MYSQLND_STATS ** stats, const size_t statistic_count, const b
226233
PHPAPI void
227234
mysqlnd_stats_end(MYSQLND_STATS * stats, const bool persistent)
228235
{
229-
#ifdef ZTS
230-
tsrm_mutex_free(stats->LOCK_access);
231-
#endif
232-
/* mnd_free will reference LOCK_access and crash...*/
236+
/* mnd_free would update the statistics being freed */
233237
pefree(stats, persistent);
234238
}
235239
/* }}} */

‎ext/mysqlnd/mysqlnd_statistics.h‎

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,12 @@
1717
#define MYSQLND_STATISTICS_H
1818

1919
#ifdef ZTS
20-
#define MYSQLND_STATS_LOCK(stats) tsrm_mutex_lock((stats)->LOCK_access)
21-
#define MYSQLND_STATS_UNLOCK(stats) tsrm_mutex_unlock((stats)->LOCK_access)
20+
#define MYSQLND_STATS_UPDATE_VALUE(stats, statistic, value) \
21+
atomic_fetch_add_explicit(&(stats)->values[(statistic)], (value), memory_order_relaxed)
2222
#else
23-
#define MYSQLND_STATS_LOCK(stats)
24-
#define MYSQLND_STATS_UNLOCK(stats)
25-
#endif
26-
2723
#define MYSQLND_STATS_UPDATE_VALUE(stats, statistic, value) \
28-
{ \
29-
MYSQLND_STATS_LOCK(stats); \
30-
(stats)->values[(statistic)] += (value); \
31-
MYSQLND_STATS_UNLOCK(_p_s); \
32-
}
24+
((stats)->values[(statistic)] += (value))
25+
#endif
3326

3427
#define MYSQLND_DEC_STATISTIC(enabler, stats, statistic) \
3528
{ \

‎ext/mysqlnd/mysqlnd_structs.h‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,13 +292,24 @@ typedef enum_func_status (*mysqlnd_fetch_row_func)(MYSQLND_RES *result,
292292

293293
typedef struct st_mysqlnd_stats MYSQLND_STATS;
294294

295+
#if defined(ZTS) && defined(__cplusplus)
296+
/* The C11 atomics used by struct st_mysqlnd_stats and the MYSQLND_*_STATISTIC*() macros */
297+
extern "C++" {
298+
using std::atomic_uint_least64_t;
299+
using std::atomic_fetch_add_explicit;
300+
using std::atomic_load_explicit;
301+
using std::memory_order_relaxed;
302+
}
303+
#endif
304+
295305
struct st_mysqlnd_stats
296306
{
297307
size_t count;
298308
#ifdef ZTS
299-
MUTEX_T LOCK_access;
300-
#endif
309+
atomic_uint_least64_t values[] ZEND_ELEMENT_COUNT(count);
310+
#else
301311
uint64_t values[] ZEND_ELEMENT_COUNT(count);
312+
#endif
302313
};
303314

304315

0 commit comments

Comments
 (0)