Adding `final class Deque` to PHP
| From: | tyson andre | Date: | Mon, 20 Sep 2021 00:03:33 +0000 |
| Subject: | Adding `final class Deque` to PHP | ||
| Groups: | php.internals | ||
| Request: | Send a blank email to internals+get-116100@lists.php.net to get a copy of this message | ||
Hi internals,
I've created a new RFC https://wiki.php.net/rfc/deque to add a
final class
Deque
This is based on the Teds\Deque implementation I've worked on
for the https://github.com/TysonAndre/pecl-teds PECL.
While SplDoublyLinkedList and its subclass SplQueue/SplStack
exist in the SPL, they have several drawbacks
that are addressed by this RFC to add a Deque class (to use instead of those):
1. SplDoublyLinkedList is internally represented by a doubly linked list,
making it use roughly twice as much memory as the proposed Deque
2. push/pop/unshift/shift from
SplDoublyLinkedList are slower due to
needing to allocate or free the linked list nodes.
3. Reading values in the middle of the SplDoublyLinkedList is proportional to the
length of the list,
due to needing to traverse the linked list nodes.
4. foreach Iteration behavior cannot be understood without knowing what constructed the
SplDoublyLinkedList instance or set the flags.
It would be useful to have an efficient Deque container in the standard library
to provide an alternative without those drawbacks,
as well as for the following reasons:
1. To save memory in applications or libraries that may need to store many lists of values or run
for long periods of time.
Notably, PHP's array type will never release allocated capacity.
See https://www.npopov.com/2014/12/22/PHPs-new-hashtable-implementation.html
2. To provide a better alternative to SplDoublyLinkedList, SplStack, and
SplQueue
for use cases that require stacks or queues.
3. As a more efficient option than array and SplDoublyLinkedList
as a queue or Deque, especially for unshift.
A Deque is more efficient than an array when used as a queue, more
readable, and easier to use correctly.
While it is possible to efficiently remove elements from the start of an array (in
terms of insertion order),
it is very inefficient to prepend elements to the start of a large array due to needing
to either copy the array
or move all elements in the internal array representation,
and an array would use much more memory than a Deque when used that way
(and be slower).
There are also several pitfalls to using an array as a queue for larger queue sizes,
some of which are not obvious and discovered while writing the benchmarks.
(Having a better (double-ended) queue datastructure (Deque) than the
SplDoublyLinkedList
would save users from needing to write code with these pitfalls):
1. array_key_first() takes time proportional to the number of elements
unset from the start of an array,
causing it to unexpectedly be extremely slow (quadratic time) after unsetting many elements at
the start of the queue.
(when the array infrequently runs out of capacity, buckets are moved to the front)
2. reset() or end() will convert a variable to a reference,
and php is significantly less efficient at reading or writing to reference.
Opcache is also less efficient at optimizing uses of variables using references.
3. More obviously, array_unshift and array_shift will take time
proportional to the number of elements in the array
(to reindex and move existing/remaining elements).
After the discussion period ends, I currently plan to start voting on the Deque RFC and
await the results
to determine next steps for the Vector RFC.
----
The thread for my other open proposal final class Vector
(https://externals.io/message/116048) has prior discussion on implementation choices,
naming choices, and motivation for adding datastructures to php-src.
- E.g. the question of why we should add general-purpose datastructures
to php-src itself rather than have users rely on PECLs,
and why this proposal doesn't and can't use php-ds/ext-ds.
Thanks,
Tyson