Skip to content

SplPriorityQueue performance improvements #6859

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 12, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion ext/spl/spl_heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,15 @@ static zend_always_inline void *spl_heap_elem(spl_ptr_heap *heap, size_t i) {

static zend_always_inline void spl_heap_elem_copy(spl_ptr_heap *heap, void *to, void *from) {
assert(to != from);
memcpy(to, from, heap->elem_size);

/* Specialized for cases of heap and priority queue. With the size being
* constant known at compile time the compiler can fully inline calls to memcpy. */
if (heap->elem_size == sizeof(spl_pqueue_elem)) {
memcpy(to, from, sizeof(spl_pqueue_elem));
} else {
ZEND_ASSERT(heap->elem_size == sizeof(zval));
memcpy(to, from, sizeof(zval));
}
}

static void spl_ptr_heap_zval_dtor(void *elem) { /* {{{ */
Expand Down Expand Up @@ -237,6 +245,22 @@ static int spl_ptr_pqueue_elem_cmp(void *x, void *y, zval *object) { /* {{{ */
}
/* }}} */

/* Specialized comparator used when we are absolutely sure an instance of the
* not inherited SplPriorityQueue class contains only priorities as longs. This
* fact is tracked during insertion into the queue. */
static int spl_ptr_pqueue_elem_cmp_long(void *x, void *y, zval *object) {
zend_long a = Z_LVAL(((spl_pqueue_elem*) x)->priority);
zend_long b = Z_LVAL(((spl_pqueue_elem*) y)->priority);
return a>b ? 1 : (a<b ? -1 : 0);
}

/* same as spl_ptr_pqueue_elem_cmp_long */
static int spl_ptr_pqueue_elem_cmp_double(void *x, void *y, zval *object) {
double a = Z_DVAL(((spl_pqueue_elem*) x)->priority);
double b = Z_DVAL(((spl_pqueue_elem*) y)->priority);
return ZEND_NORMALIZE_BOOL(a - b);
}

static spl_ptr_heap *spl_ptr_heap_init(spl_ptr_heap_cmp_func cmp, spl_ptr_heap_ctor_func ctor, spl_ptr_heap_dtor_func dtor, size_t elem_size) /* {{{ */
{
spl_ptr_heap *heap = emalloc(sizeof(spl_ptr_heap));
Expand Down Expand Up @@ -653,6 +677,21 @@ PHP_METHOD(SplPriorityQueue, insert)
ZVAL_COPY(&elem.data, data);
ZVAL_COPY(&elem.priority, priority);

/* If we know this call came from non inherited SplPriorityQueue it's
* possible to do specialization on the type of the priority parameter. */
if (!intern->fptr_cmp) {
int type = Z_TYPE(elem.priority);
spl_ptr_heap_cmp_func new_cmp =
(type == IS_LONG) ? spl_ptr_pqueue_elem_cmp_long :
((type == IS_DOUBLE) ? spl_ptr_pqueue_elem_cmp_double : spl_ptr_pqueue_elem_cmp);

if (intern->heap->count == 0) { /* Specialize empty queue */
intern->heap->cmp = new_cmp;
} else if (new_cmp != intern->heap->cmp) { /* Despecialize on type conflict. */
intern->heap->cmp = spl_ptr_pqueue_elem_cmp;
}
}

spl_ptr_heap_insert(intern->heap, &elem, ZEND_THIS);

RETURN_TRUE;
Expand Down