1
0
mirror of https://github.com/php/php-src.git synced 2026-04-20 22:41:20 +02:00

Optimize SplPriorityQueue implementation

Do not create an array with two keys for every element. That's a
huge waste of memory. Instead we allocate a two-element structure.
This reduces memory usage and avoids having to perform hashtable
lookups to get at the data and priority.

The only thing this might impact negatively is the non-default
EXTR_BOTH mode, in which case the array has to be created anyway.
This commit is contained in:
Nikita Popov
2018-11-15 20:49:37 +01:00
parent 0f7f1498be
commit e965fee904

View File

@@ -80,6 +80,11 @@ struct _spl_heap_it {
int flags;
};
typedef struct _spl_pqueue_elem {
zval data;
zval priority;
} spl_pqueue_elem;
static inline spl_heap_object *spl_heap_from_obj(zend_object *obj) /* {{{ */ {
return (spl_heap_object*)((char*)(obj) - XtOffsetOf(spl_heap_object, std));
}
@@ -99,6 +104,23 @@ static void spl_ptr_heap_zval_ctor(zval *elem) { /* {{{ */
}
/* }}} */
static void spl_ptr_heap_pqueue_elem_dtor(zval *zv) { /* {{{ */
spl_pqueue_elem *elem = Z_PTR_P(zv);
zval_ptr_dtor(&elem->data);
zval_ptr_dtor(&elem->priority);
efree(elem);
}
/* }}} */
static void spl_ptr_heap_pqueue_elem_ctor(zval *zv) { /* {{{ */
spl_pqueue_elem *old_elem = Z_PTR_P(zv);
spl_pqueue_elem *new_elem = emalloc(sizeof(spl_pqueue_elem));
ZVAL_COPY(&new_elem->data, &old_elem->data);
ZVAL_COPY(&new_elem->priority, &old_elem->priority);
Z_PTR_P(zv) = new_elem;
}
/* }}} */
static int spl_ptr_heap_cmp_cb_helper(zval *object, spl_heap_object *heap_object, zval *a, zval *b, zend_long *result) { /* {{{ */
zval zresult;
@@ -115,25 +137,27 @@ static int spl_ptr_heap_cmp_cb_helper(zval *object, spl_heap_object *heap_object
}
/* }}} */
static zval *spl_pqueue_extract_helper(zval *value, int flags) /* {{{ */
static int spl_pqueue_extract_helper(zval *result, zval *value, int flags) /* {{{ */
{
spl_pqueue_elem *elem = Z_PTR_P(value);
if ((flags & SPL_PQUEUE_EXTR_BOTH) == SPL_PQUEUE_EXTR_BOTH) {
return value;
} else if ((flags & SPL_PQUEUE_EXTR_BOTH) > 0) {
if ((flags & SPL_PQUEUE_EXTR_DATA) == SPL_PQUEUE_EXTR_DATA) {
zval *data;
if ((data = zend_hash_str_find(Z_ARRVAL_P(value), "data", sizeof("data") - 1)) != NULL) {
return data;
}
} else {
zval *priority;
if ((priority = zend_hash_str_find(Z_ARRVAL_P(value), "priority", sizeof("priority") - 1)) != NULL) {
return priority;
}
}
array_init(result);
add_assoc_zval_ex(result, "data", sizeof("data") - 1, &elem->data);
add_assoc_zval_ex(result, "priority", sizeof("priority") - 1, &elem->priority);
return SUCCESS;
}
return NULL;
if (flags & SPL_PQUEUE_EXTR_DATA) {
ZVAL_COPY(result, &elem->data);
return SUCCESS;
}
if (flags & SPL_PQUEUE_EXTR_PRIORITY) {
ZVAL_COPY(result, &elem->priority);
return SUCCESS;
}
return FAILURE;
}
/* }}} */
@@ -185,15 +209,12 @@ static int spl_ptr_heap_zval_min_cmp(zval *a, zval *b, zval *object) { /* {{{ */
}
/* }}} */
static int spl_ptr_pqueue_zval_cmp(zval *a, zval *b, zval *object) { /* {{{ */
static int spl_ptr_pqueue_elem_cmp(zval *a_zv, zval *b_zv, zval *object) { /* {{{ */
spl_pqueue_elem *a = Z_PTR_P(a_zv);
spl_pqueue_elem *b = Z_PTR_P(b_zv);
zval *a_priority_p = &a->priority;
zval *b_priority_p = &b->priority;
zval result;
zval *a_priority_p = spl_pqueue_extract_helper(a, SPL_PQUEUE_EXTR_PRIORITY);
zval *b_priority_p = spl_pqueue_extract_helper(b, SPL_PQUEUE_EXTR_PRIORITY);
if ((!a_priority_p) || (!b_priority_p)) {
zend_error(E_RECOVERABLE_ERROR, "Unable to extract from the PriorityQueue node");
return 0;
}
if (EG(exception)) {
return 0;
@@ -203,7 +224,7 @@ static int spl_ptr_pqueue_zval_cmp(zval *a, zval *b, zval *object) { /* {{{ */
spl_heap_object *heap_object = Z_SPLHEAP_P(object);
if (heap_object->fptr_cmp) {
zend_long lval = 0;
if (spl_ptr_heap_cmp_cb_helper((zval *)object, heap_object, a_priority_p, b_priority_p, &lval) == FAILURE) {
if (spl_ptr_heap_cmp_cb_helper(object, heap_object, a_priority_p, b_priority_p, &lval) == FAILURE) {
/* exception or call failure */
return 0;
}
@@ -388,7 +409,9 @@ static zend_object *spl_heap_object_new_ex(zend_class_entry *class_type, zval *o
while (parent) {
if (parent == spl_ce_SplPriorityQueue) {
intern->heap->cmp = spl_ptr_pqueue_zval_cmp;
intern->heap->cmp = spl_ptr_pqueue_elem_cmp;
intern->heap->ctor = spl_ptr_heap_pqueue_elem_ctor;
intern->heap->dtor = spl_ptr_heap_pqueue_elem_dtor;
intern->flags = SPL_PQUEUE_EXTR_DATA;
intern->std.handlers = &spl_handler_SplPriorityQueue;
break;
@@ -502,9 +525,13 @@ static HashTable* spl_heap_object_get_debug_info_helper(zend_class_entry *ce, zv
array_init(&heap_array);
for (i = 0; i < intern->heap->count; ++i) {
add_index_zval(&heap_array, i, &intern->heap->elements[i]);
if (Z_REFCOUNTED(intern->heap->elements[i])) {
Z_ADDREF(intern->heap->elements[i]);
if (ce == spl_ce_SplPriorityQueue) {
zval elem;
spl_pqueue_extract_helper(&elem, &intern->heap->elements[i], SPL_PQUEUE_EXTR_BOTH);
add_index_zval(&heap_array, i, &elem);
} else {
add_index_zval(&heap_array, i, &intern->heap->elements[i]);
Z_TRY_ADDREF(intern->heap->elements[i]);
}
}
@@ -623,8 +650,9 @@ SPL_METHOD(SplHeap, extract)
Push $value with the priority $priodiry on the priorityqueue */
SPL_METHOD(SplPriorityQueue, insert)
{
zval *data, *priority, elem;
zval *data, *priority, elem_zv;
spl_heap_object *intern;
spl_pqueue_elem *elem;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &data, &priority) == FAILURE) {
return;
@@ -637,14 +665,12 @@ SPL_METHOD(SplPriorityQueue, insert)
return;
}
Z_TRY_ADDREF_P(data);
Z_TRY_ADDREF_P(priority);
elem = emalloc(sizeof(spl_pqueue_elem));
ZVAL_COPY(&elem->data, data);
ZVAL_COPY(&elem->priority, priority);
ZVAL_PTR(&elem_zv, elem);
array_init(&elem);
add_assoc_zval_ex(&elem, "data", sizeof("data") - 1, data);
add_assoc_zval_ex(&elem, "priority", sizeof("priority") - 1, priority);
spl_ptr_heap_insert(intern->heap, &elem, ZEND_THIS);
spl_ptr_heap_insert(intern->heap, &elem_zv, ZEND_THIS);
RETURN_TRUE;
}
@@ -675,16 +701,11 @@ SPL_METHOD(SplPriorityQueue, extract)
return;
}
value_out = spl_pqueue_extract_helper(&value, intern->flags);
if (!value_out) {
if (spl_pqueue_extract_helper(return_value, &value, intern->flags) == FAILURE) {
zend_error(E_RECOVERABLE_ERROR, "Unable to extract from the PriorityQueue node");
zval_ptr_dtor(&value);
spl_ptr_heap_pqueue_elem_dtor(&value);
return;
}
ZVAL_COPY_DEREF(return_value, value_out);
zval_ptr_dtor(&value);
}
/* }}} */
@@ -713,14 +734,9 @@ SPL_METHOD(SplPriorityQueue, top)
return;
}
value_out = spl_pqueue_extract_helper(value, intern->flags);
if (!value_out) {
if (spl_pqueue_extract_helper(return_value, value, intern->flags) == FAILURE) {
zend_error(E_RECOVERABLE_ERROR, "Unable to extract from the PriorityQueue node");
return;
}
ZVAL_COPY_DEREF(return_value, value_out);
}
/* }}} */
@@ -906,6 +922,7 @@ static zval *spl_heap_it_get_current_data(zend_object_iterator *iter) /* {{{ */
static zval *spl_pqueue_it_get_current_data(zend_object_iterator *iter) /* {{{ */
{
zend_user_iterator *user_it = (zend_user_iterator *) iter;
spl_heap_object *object = Z_SPLHEAP_P(&iter->data);
zval *element = &object->heap->elements[0];
@@ -916,13 +933,13 @@ static zval *spl_pqueue_it_get_current_data(zend_object_iterator *iter) /* {{{ *
if (object->heap->count == 0 || Z_ISUNDEF_P(element)) {
return NULL;
} else {
zval *data = spl_pqueue_extract_helper(element, object->flags);
if (!data) {
}
if (Z_ISUNDEF(user_it->value)) {
if (spl_pqueue_extract_helper(&user_it->value, element, object->flags) == FAILURE) {
zend_error(E_RECOVERABLE_ERROR, "Unable to extract from the PriorityQueue node");
}
return data;
}
return &user_it->value;
}
/* }}} */
@@ -1040,14 +1057,10 @@ SPL_METHOD(SplPriorityQueue, current)
if (!intern->heap->count || Z_ISUNDEF_P(element)) {
RETURN_NULL();
} else {
zval *data = spl_pqueue_extract_helper(element, intern->flags);
if (!data) {
if (spl_pqueue_extract_helper(return_value, element, intern->flags) == FAILURE) {
zend_error(E_RECOVERABLE_ERROR, "Unable to extract from the PriorityQueue node");
RETURN_NULL();
}
ZVAL_COPY_DEREF(return_value, data);
}
}
/* }}} */