Skip to content

Commit 9eca4dc

Browse files
committed
Optimized memory handling of BIT fields. Less memory copies and less
memory usage.
1 parent 3836c2f commit 9eca4dc

File tree

6 files changed

+15
-91
lines changed

6 files changed

+15
-91
lines changed

NEWS

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ PHP NEWS
1616
. Fixed bug #73646 (mb_ereg_search_init null pointer dereference).
1717
(Laruence)
1818

19+
- Mysqlnd:
20+
. Optimized handling of BIT fields - less memory copies and lower memory
21+
usage. (Andrey)
22+
1923
- Opcache:
2024
. Fixed bug #73654 (Segmentation fault in zend_call_function). (Nikita)
2125
. Fixed bug #73668 ("SIGFPE Arithmetic exception" in opcache when divide by

ext/mysqlnd/mysqlnd_result.c

-4
Original file line numberDiff line numberDiff line change
@@ -968,8 +968,6 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, const zend_b
968968
row_packet->field_count = result->field_count;
969969
row_packet->binary_protocol = ps;
970970
row_packet->fields_metadata = result->meta->fields;
971-
row_packet->bit_fields_count = result->meta->bit_fields_count;
972-
row_packet->bit_fields_total_len = result->meta->bit_fields_total_len;
973971

974972
result->unbuf->row_packet = row_packet;
975973
}
@@ -1322,8 +1320,6 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
13221320
row_packet->field_count = meta->field_count;
13231321
row_packet->binary_protocol = binary_protocol;
13241322
row_packet->fields_metadata = meta->fields;
1325-
row_packet->bit_fields_count = meta->bit_fields_count;
1326-
row_packet->bit_fields_total_len = meta->bit_fields_total_len;
13271323

13281324
row_packet->skip_extraction = TRUE; /* let php_mysqlnd_rowp_read() not allocate row_packet->fields, we will do it */
13291325

ext/mysqlnd/mysqlnd_result_meta.c

-34
Original file line numberDiff line numberDiff line change
@@ -90,40 +90,6 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met
9090
PACKET_FREE(field_packet);
9191
DBG_RETURN(FAIL);
9292
}
93-
if (meta->fields[i].type == MYSQL_TYPE_BIT) {
94-
size_t field_len;
95-
DBG_INF("BIT");
96-
++meta->bit_fields_count;
97-
/* .length is in bits */
98-
field_len = meta->fields[i].length / 8;
99-
/*
100-
If there is rest, add one byte :
101-
8 bits = 1 byte but 9 bits = 2 bytes
102-
*/
103-
if (meta->fields[i].length % 8) {
104-
++field_len;
105-
}
106-
switch (field_len) {
107-
case 8:
108-
case 7:
109-
case 6:
110-
case 5:
111-
meta->bit_fields_total_len += 20;/* 21 digis, no sign*/
112-
break;
113-
case 4:
114-
meta->bit_fields_total_len += 10;/* 2 000 000 000*/
115-
break;
116-
case 3:
117-
meta->bit_fields_total_len += 8;/* 12 000 000*/
118-
break;
119-
case 2:
120-
meta->bit_fields_total_len += 5;/* 32 500 */
121-
break;
122-
case 1:
123-
meta->bit_fields_total_len += 3;/* 120 */
124-
break;
125-
}
126-
}
12793

12894
/* For BC we have to check whether the key is numeric and use it like this */
12995
if ((meta->zend_hash_keys[i].is_numeric = ZEND_HANDLE_NUMERIC(field_packet->metadata->sname, idx))) {

ext/mysqlnd/mysqlnd_structs.h

-4
Original file line numberDiff line numberDiff line change
@@ -1150,10 +1150,6 @@ struct st_mysqlnd_result_metadata
11501150

11511151
MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res_meta) * m;
11521152

1153-
size_t bit_fields_total_len; /* trailing \0 not counted */
1154-
/* We need this to make fast allocs in rowp_read */
1155-
unsigned int bit_fields_count;
1156-
11571153
unsigned int current_field;
11581154
unsigned int field_count;
11591155

ext/mysqlnd/mysqlnd_wireprotocol.c

+11-46
Original file line numberDiff line numberDiff line change
@@ -1460,8 +1460,7 @@ php_mysqlnd_read_row_ex(MYSQLND_PFC * pfc,
14601460
MYSQLND_ERROR_INFO * error_info,
14611461
MYSQLND_MEMORY_POOL * pool,
14621462
MYSQLND_MEMORY_POOL_CHUNK ** buffer,
1463-
size_t * data_size, zend_bool persistent_alloc,
1464-
unsigned int prealloc_more_bytes)
1463+
size_t * data_size, zend_bool persistent_alloc)
14651464
{
14661465
enum_func_status ret = PASS;
14671466
MYSQLND_PACKET_HEADER header;
@@ -1478,7 +1477,7 @@ php_mysqlnd_read_row_ex(MYSQLND_PFC * pfc,
14781477
zero-length byte, don't read the body, there is no such.
14791478
*/
14801479

1481-
*data_size = prealloc_more_bytes;
1480+
*data_size = 0;
14821481
while (1) {
14831482
if (FAIL == mysqlnd_read_header(pfc, vio, &header, stats, error_info)) {
14841483
ret = FAIL;
@@ -1527,7 +1526,6 @@ php_mysqlnd_read_row_ex(MYSQLND_PFC * pfc,
15271526
pool->free_chunk(pool, *buffer);
15281527
*buffer = NULL;
15291528
}
1530-
*data_size -= prealloc_more_bytes;
15311529
DBG_RETURN(ret);
15321530
}
15331531
/* }}} */
@@ -1634,8 +1632,6 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer,
16341632
zval *current_field, *end_field, *start_field;
16351633
zend_uchar * p = row_buffer->ptr;
16361634
size_t data_size = row_buffer->app;
1637-
/* we allocate from here. In pre-7.0 it was +1, as there was an additional \0 for the last string in the packet - because of the zval optimizations - using no-copy */
1638-
zend_uchar * bit_area = (zend_uchar*) row_buffer->ptr + data_size;
16391635
const zend_uchar * const packet_end = (zend_uchar*) row_buffer->ptr + data_size;
16401636

16411637
DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_aux");
@@ -1746,46 +1742,22 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer,
17461742
#endif /* MYSQLND_STRING_TO_INT_CONVERSION */
17471743
if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
17481744
/*
1749-
BIT fields are specially handled. As they come as bit mask, we have
1750-
to convert it to human-readable representation. As the bits take
1751-
less space in the protocol than the numbers they represent, we don't
1752-
have enough space in the packet buffer to overwrite inside.
1753-
Thus, a bit more space is pre-allocated at the end of the buffer,
1754-
see php_mysqlnd_rowp_read(). And we add the strings at the end.
1755-
Definitely not nice, _hackish_ :(, but works.
1745+
BIT fields are specially handled. As they come as bit mask, they have
1746+
to be converted to human-readable representation.
17561747
*/
1757-
zend_uchar *start = bit_area;
17581748
ps_fetch_from_1_to_8_bytes(current_field, &(fields_metadata[i]), 0, (const zend_uchar **) &p, len);
17591749
/*
17601750
We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
17611751
later in this function there will be an advancement.
17621752
*/
17631753
p -= len;
1764-
if (Z_TYPE_P(current_field) == IS_LONG) {
1765-
/*
1766-
Andrey : See below. No need of bit_area, as we can use on stack for this.
1767-
The bit area should be removed - the `prealloc_more_bytes` in php_mysqlnd_read_row_ex()
1768-
1769-
char tmp[22];
1770-
const size_t tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, Z_LVAL_P(current_field));
1771-
ZVAL_STRINGL(current_field, tmp, tmp_len);
1772-
*/
1773-
bit_area += 1 + sprintf((char *)start, ZEND_LONG_FMT, Z_LVAL_P(current_field));
1774-
ZVAL_STRINGL(current_field, (char *) start, bit_area - start - 1);
1754+
if (Z_TYPE_P(current_field) == IS_LONG && !as_int_or_float) {
1755+
/* we are using the text protocol, so convert to string */
1756+
char tmp[22];
1757+
const size_t tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, Z_LVAL_P(current_field));
1758+
ZVAL_STRINGL(current_field, tmp, tmp_len);
17751759
} else if (Z_TYPE_P(current_field) == IS_STRING) {
1776-
/*
1777-
Andrey : This is totally sensless, but I am not gonna remove it in a production version.
1778-
This copies the data from the zval to the bit area. The destroys the original value
1779-
and creates the same one from the bit area. No need. It was making sense in pre-7.0
1780-
when we used zval IS_STRING with no-copy that referred to the bit area.
1781-
The bit area has no sense in both the case of IS_LONG and IS_STRING as 7.0 zval
1782-
IS_STRING always copies.
1783-
*/
1784-
memcpy(bit_area, Z_STRVAL_P(current_field), Z_STRLEN_P(current_field));
1785-
bit_area += Z_STRLEN_P(current_field);
1786-
*bit_area++ = '\0';
1787-
zval_dtor(current_field);
1788-
ZVAL_STRINGL(current_field, (char *) start, bit_area - start - 1);
1760+
/* nothing to do here, as we want a string and ps_fetch_from_1_to_8_bytes() has given us one */
17891761
}
17901762
} else {
17911763
ZVAL_STRINGL(current_field, (char *)p, len);
@@ -1842,20 +1814,13 @@ php_mysqlnd_rowp_read(void * _packet)
18421814
MYSQLND_STATS * stats = packet->header.stats;
18431815
zend_uchar *p;
18441816
enum_func_status ret = PASS;
1845-
size_t post_alloc_for_bit_fields = 0;
18461817
size_t data_size = 0;
18471818

18481819
DBG_ENTER("php_mysqlnd_rowp_read");
18491820

1850-
if (!packet->binary_protocol && packet->bit_fields_count) {
1851-
/* For every field we need terminating \0 */
1852-
post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count;
1853-
}
1854-
18551821
ret = php_mysqlnd_read_row_ex(pfc, vio, stats, error_info,
18561822
packet->result_set_memory_pool, &packet->row_buffer, &data_size,
1857-
packet->persistent_alloc, post_alloc_for_bit_fields
1858-
);
1823+
packet->persistent_alloc);
18591824
if (FAIL == ret) {
18601825
goto end;
18611826
}

ext/mysqlnd/mysqlnd_wireprotocol.h

-3
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,6 @@ typedef struct st_mysqlnd_packet_row {
235235
zend_bool binary_protocol;
236236
zend_bool persistent_alloc;
237237
MYSQLND_FIELD *fields_metadata;
238-
/* We need this to alloc bigger bufs in non-PS mode */
239-
unsigned int bit_fields_count;
240-
size_t bit_fields_total_len; /* trailing \0 not counted */
241238

242239
/* If error packet, we use these */
243240
MYSQLND_ERROR_INFO error_info;

0 commit comments

Comments
 (0)