Comparing PHP Collections

Let’s imagine that we want to store a collection of Item objects:

<?php

namespace App\Entity;

class Item {
public string $name = ”;
public int $value = 0;
}

Working with this collection we want to:

PHP does a type checks to mak…


This content originally appeared on DEV Community and was authored by Anton Sukhachev

Let's imagine that we want to store a collection of Item objects:

<?php

namespace App\Entity;

class Item {
    public string $name = '';
    public int $value = 0;
}

Working with this collection we want to:

  • PHP does a type checks to make sure collection only stores Item objects
  • IDE makes hints about the type of Item objects in a collection
  • static analyses tool can find type errors

I want to compare:

Usage

Let's first compare usage.

Array

<?php

/** @var Item[] $collection */
$collection = [];
$collection[] = new Item();

/**
 * @param Item[] $collection
 */
function test(array $collection): array {}

pros

  • easy to implement
  • IDE makes hints about the type of Item objects in collection
  • static analyses tool can find type errors

cons

  • PHP doesn't check types

Psalm annotations

<?php

/**
 * @template T
 */
class Collection implements \Iterator
{
    protected \ArrayIterator $iterator;

    public function __construct()
    {
        $this->iterator = new \ArrayIterator();
    }

    /** @param T $item */
    public function append($item): void
    {
        $this->iterator->append($item);
    }

    /** @return T|null */
    public function current(): mixed
    {
        return $this->iterator->current();
    }
...
}
<?php
/** @var Collection<Item> $collection */
$collection = new Collection();
$collection->append(new Item());

/**
 * @param Collection<Item> $data
 * @return Collection<Item>
 */
function test(Collection $data): Collection {}

pros

  • IDE makes hints about the type of Item objects in collection
  • static analyses tool can find type errors

cons

  • PHP doesn't check types

Monomorphic generics

<?php

class Collection<T> implements \Iterator
{
    protected \ArrayIterator $iterator;

    public function __construct()
    {
        $this->iterator = new \ArrayIterator();
    }

    public function append(T $item): void
    {
        $this->iterator->append($item);
    }

    public function current(): ?T
    {
        return $this->iterator->current();
    }
..
}
<?php

$collection = new Collection<Item>;
$collection->append(new Item());

function test(Collection<Item> $data): Collection<Item> {}

pros

  • PHP does a type checks to make sure collection only stores Item objects

cons

  • static analyses tool can't find type errors
  • IDE doesn't make hints about the type of Item objects in a collection

Type erased generics

<?php

class Collection<T> implements \Iterator
{
    protected \ArrayIterator $iterator;

    public function __construct()
    {
        $this->iterator = new \ArrayIterator();
    }

    public function append(T $item): void
    {
        $this->iterator->append($item);
    }

    public function current(): ?T
    {
        return $this->iterator->current();
    }
...
}
<?php

$collection = new Collection<Item>;
$collection->append(new Item());

function test(Collection<Item> $data): Collection<Item> {}

cons

  • PHP doesn't check types
  • static analyses tool can't find type errors
  • IDE doesn't make hints about the type of Item objects in a collection

Memory

Memory test is very simple.
It creates $collection and puts Item objects into it count times.
For measurement I use var_sizeof() and memory_get_usage().

Items in collection: 0

type var_class_sizeof(bytes) var_sizeof(bytes) memory_get_usage(bytes)
array(count: 0) 0 336 0
psalm(count: 0) 1,510 544 240
monomorphic(count: 0) 1,528 544 240
type-erased(count: 0) 1,512 544 240

Items in collection: 100

type var_class_sizeof(bytes) var_sizeof(bytes) memory_get_usage(bytes)
array(count: 100) 0 7,728 12,248
psalm(count: 100) 1,510 7,936 12,432
monomorphic(count: 100) 1,528 7,936 12,432
type-erased(count: 100) 1,512 7,936 12,432

Items in collection: 1000

type var_class_sizeof(bytes) var_sizeof(bytes) memory_get_usage(bytes)
array(count: 1000) 0 72,464 76,920
psalm(count: 1000) 1,510 72,672 77,104
monomorphic(count: 1000) 1,528 72,672 77,104
type-erased(count: 1000) 1,512 72,672 77,104

Items in collection: 10000

type var_class_sizeof(bytes) var_sizeof(bytes) memory_get_usage(bytes)
array(count: 10000) 0 822,224 1,051,320
psalm(count: 10000) 1,510 822,432 1,051,560
monomorphic(count: 10000) 1,528 822,432 1,051,560
type-erased(count: 10000) 1,512 822,432 1,051,560

It's not surprising an array requires the least amount of memory.
Other approaches have roughly the same internal structure and require the same amount of memory.

Performance

I used phpbench to measure performance.
I think one of the big differences between all the solutions is the type checking. So I decided to try measure it.
How much faster will code with/without type checking?

PHPBench (1.2.3) running benchmarks...
with configuration file: /app/phpbench.json
with PHP version 8.1.3, xdebug ❌, opcache ❌

\App\Tests\TypHintBench

    benchWithoutType........................R1 I6 - Mo240.713μs (±0.47%)
    benchWithArrayType......................R1 I70 - Mo247.663μs (±0.45%)
    benchWithMixedType......................R2 I59 - Mo249.293μs (±0.54%)
    benchWithClassType......................R1 I26 - Mo306.533μs (±0.48%)

Subjects: 4, Assertions: 0, Failures: 0, Errors: 0
+--------------+--------------------+-----+------+-----+-----------+-----------+--------+
| benchmark    | subject            | set | revs | its | mem_peak  | mode      | rstdev |
+--------------+--------------------+-----+------+-----+-----------+-----------+--------+
| TypHintBench | benchWithoutType   |     | 1000 | 100 | 674.272kb | 240.713μs | ±0.47% |
| TypHintBench | benchWithArrayType |     | 1000 | 100 | 674.272kb | 247.663μs | ±0.45% |
| TypHintBench | benchWithMixedType |     | 1000 | 100 | 674.272kb | 249.293μs | ±0.54% |
| TypHintBench | benchWithClassType |     | 1000 | 100 | 674.272kb | 306.533μs | ±0.48% |
+--------------+--------------------+-----+------+-----+-----------+-----------+--------+

If the tests are correct, the difference between a function argument with class type and a function argument without a type is 30%.

Conclusions

I have drawn some charts so you can compare all measurements in percentage. The results may vary slightly on different machines, but the percentage result should be the same

PHP type checking time

PHP memory usage

If you are only using simple collections you can use array - it is very fast and requires little memory.
PHP doesn't support generic syntax and IDE can't make hints about the type of objects in a collection (yet),
but if you want PHP to do type checks at runtime you can test the php-generics library.

I think for now the best solution is a combination of array with annotations for simple collections and Psalm annotation templates for complex objects.

If you want to reproduce the tests result you can find the source code and instructions here.


This content originally appeared on DEV Community and was authored by Anton Sukhachev


Print Share Comment Cite Upload Translate Updates
APA

Anton Sukhachev | Sciencx (2022-03-22T08:04:38+00:00) Comparing PHP Collections. Retrieved from https://www.scien.cx/2022/03/22/comparing-php-collections/

MLA
" » Comparing PHP Collections." Anton Sukhachev | Sciencx - Tuesday March 22, 2022, https://www.scien.cx/2022/03/22/comparing-php-collections/
HARVARD
Anton Sukhachev | Sciencx Tuesday March 22, 2022 » Comparing PHP Collections., viewed ,<https://www.scien.cx/2022/03/22/comparing-php-collections/>
VANCOUVER
Anton Sukhachev | Sciencx - » Comparing PHP Collections. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/03/22/comparing-php-collections/
CHICAGO
" » Comparing PHP Collections." Anton Sukhachev | Sciencx - Accessed . https://www.scien.cx/2022/03/22/comparing-php-collections/
IEEE
" » Comparing PHP Collections." Anton Sukhachev | Sciencx [Online]. Available: https://www.scien.cx/2022/03/22/comparing-php-collections/. [Accessed: ]
rf:citation
» Comparing PHP Collections | Anton Sukhachev | Sciencx | https://www.scien.cx/2022/03/22/comparing-php-collections/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.