【PHP】Iterableのベンチマーク

PHP

PHPの代表的なIterableとして、配列、Iterator、Generatorの3つの単純なループについてベンチマークしてみました。

前提条件

使用したIterator

<?php

namespace MyLoop;

class MyIteratorAggregate implements \IteratorAggregate
{
    private array $items;

    public function __construct(array $items = [])
    {
        $this->items = $items;
    }

    public function getIterator(): \Traversable
    {
        return new \ArrayIterator($this->items);
    }
}

Iterable供給クラス

<?php

namespace MyLoop;

require_once __DIR__ . '/MyIteratorAggregate.php';

use MyLoop\MyIteratorAggregate;

class MyIterable
{
    private int $count;

    public function __construct(int $count)
    {
        $this->count = $count;
    }

    /**
     * ゼロ埋めした配列を返す
     */
    public function getArray()
    {
        return array_fill(0, $this->count, 0);
    }

    /**
     * ゼロ埋めしたIteratorを返す
     */
    public function getIterator()
    {
        return new MyIteratorAggregate(array_fill(0, $this->count, 0));
    }

    /**
     * ゼロ埋めしたGeneratorを返す
     */
    public function getGenerator()
    {
        foreach (array_fill(0, $this->count, 0) as $item) {
            yield $item;
        }
    }
}

ベンチマークスクリプト

<?php

require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/MyIterable.php';

use Macocci7\PhpBenchmark\Benchmark;
use MyLoop\MyIterable;

$sort = true;   // 結果のソート:する(実行時間)
$desc = false;  // 結果ソート順:asc(実行時間昇順)

// 各Iterable共通の要素数
$count = 1000000;   // ひゃくまん

// Iterable供給クラスのインスタンス生成
$iterable = new MyIterable($count);

// ベンチマーク対象のコールバック
$callbacks = [
    // 配列をループ
    'array' => function ($iterable) {
        foreach ($iterable->getArray() as $item) {
            ++$item;  // とりあえず処理
        }
    },
    // Iteratorをループ
    'Iterator' => function ($iterable) {
        foreach ($iterable->getIterator() as $item) {
            ++$item;  // とりあえず処理
        }
    },
    // Generatorをループ
    'Generator' => function ($iterable) {
        foreach ($iterable->getGenerator() as $item) {
            ++$item;  // とりあえず処理
        }
    },
];

// ベンチマーク実行
$result = Benchmark::codes(
    callbacks: $callbacks,  // コールバック
    params: [$iterable],      // 各コールバック共通引数
    iteration: 1,           // 各コールバック共通実行回数
    sort: $sort,            // 結果のソート:する(実行時間)
    desc: $desc,            // 結果ソート順:asc(実行時間昇順)
);

// ベンチマーク結果出力
Benchmark::stdout($result);

ベンチマーク実行

5回実行した結果です。

▼1回目

1:     array => Time: 0.057756 sec
2:  Iterator => Time: 0.159190 sec
3: Generator => Time: 0.587436 sec

▼2回目

1:     array => Time: 0.070172 sec
2:  Iterator => Time: 0.169492 sec
3: Generator => Time: 0.588523 sec

▼3回目

1:     array => Time: 0.059871 sec
2:  Iterator => Time: 0.117324 sec
3: Generator => Time: 0.738056 sec

▼4回目

1:     array => Time: 0.095205 sec
2:  Iterator => Time: 0.122262 sec
3: Generator => Time: 0.386092 sec

▼5回目

1:     array => Time: 0.068467 sec
2:  Iterator => Time: 0.142236 sec
3: Generator => Time: 0.628917 sec

まとめ

顕著な結果が出ましたね。

▶実行時間: array < Iterator < Generator

Iterator は array の2倍~3倍程度

Generator は Iterator の3倍~5倍程度

といったところでしょうか。

コメント

タイトルとURLをコピーしました