ホームページ >バックエンド開発 >PHPチュートリアル >メモリパフォーマンスは、ジェネレーターとNikic/Iterで向上します

メモリパフォーマンスは、ジェネレーターとNikic/Iterで向上します

Joseph Gordon-Levitt
Joseph Gordon-Levittオリジナル
2025-02-16 09:17:10362ブラウズ

PHP Iterator and Generator:大規模なデータセットを効率的に処理するための強力なツール

配列と反復は、あらゆるアプリケーションの基礎です。新しいツールを入手すると、配列の使用方法も改善されるはずです。

たとえば、

ジェネレーターは新しいツールです。最初はアレイしか持っていないので、独自のクラス配列構造(Iteratorsと呼ばれる)を定義する機能を獲得します。しかし、PHP 5.5以降、発電機と呼ばれるクラスのイテレーター構造をすばやく作成できます。

Memory Performance Boosts with Generators and Nikic/Iter

ジェネレーターは関数のように見えますが、それらを反復器として使用できます。それらは、本質的に中断可能な再現可能な機能を作成するためのシンプルな構文を提供します。彼らは素晴らしいです!

ジェネレーターを使用できるいくつかの領域を検討し、発電機を使用する際に注意する必要があるいくつかの問題を調査します。最後に、才能のあるニキータポポフによって作成された素晴らしいライブラリを学びます。

サンプルコードは、https://github.com/sitepoint-editors/generators and-iterにあります。

キーポイント

  • ジェネレーター(PHP 5.5以降利用可能)は、中断可能で再現可能な機能の作成を可能にし、大規模なデータセットの処理を簡素化し、メモリパフォーマンスの改善を可能にする反復器を作成するための強力なツールです。
  • Nikita Popovは、イテレーターとジェネレーターで使用できる機能を導入するNikic/Iterライブラリを作成し、不要な中間アレイの作成を回避することで大幅にメモリを保存します。
  • ジェネレーターとNikic/Iterライブラリは、大規模なCSVファイルを使用する場合に特に役立ちます。
  • ジェネレーターはメモリパフォーマンスを大幅に改善できますが、array_filterarray_mapと互換性がないなど、Nikic/Iterなどの他のツールがそのようなデータを処理する必要があるなど、独自の課題のいくつかも提示します。

質問

多くのリレーショナルデータがあり、いくつかのプリロードを行いたいと仮定します。たぶん、データはコンマ分離されているため、各データ型をロードしてグループ化する必要があります。

次の簡単なコードから始めることができます:

<code class="language-php">function readCSV($file) {
    $rows = [];

    $handle = fopen($file, "r");

    while (!feof($handle)) {
        $rows[] = fgetcsv($handle);
    }

    fclose($handle);

    return $rows;
}

$authors = array_filter(
    readCSV("authors.csv")
);

$categories = array_filter(
    readCSV("categories.csv")
);

$posts = array_filter(
    readCSV("posts.csv")
);</code>

その後、反復または高次の関数によって関連要素を連結しようとすることができます:

<code class="language-php">function filterByColumn($array, $column, $value) {
    return array_filter(
        $array, function($item) use ($column, $value) {
            return $item[$column] == $value;
        }
    );
}

$authors = array_map(function($author) use ($posts) {
    $author["posts"] = filterByColumn(
        $posts, 1, $author[0]
    );

    // 对 $author 进行其他更改

    return $author;
}, $authors);

$categories = array_map(function($category) use ($posts) {
    $category["posts"] = filterByColumn(
        $posts, 2, $category[0]
    );

    // 对 $category 进行其他更改

    return $category;
}, $categories);

$posts = array_map(function($post) use ($authors, $categories) {
    foreach ($authors as $author) {
        if ($author[0] == $post[1]) {
            $post["author"] = $author;
            break;
        }
    }

    foreach ($categories as $category) {
        if ($category[0] == $post[1]) {
            $post["category"] = $category;
            break;
        }
    }

    // 对 $post 进行其他更改

    return $post;
}, $posts);</code>

よさそうだね?では、多数のCSVファイルを解析するとどうなりますか?メモリの使用量を少し分析しましょう...

<code class="language-php">function formatBytes($bytes, $precision = 2) {
    $kilobyte = 1024;
    $megabyte = 1024 * 1024;

    if ($bytes >= 0 && $bytes < $kilobyte) {
        return $bytes . " b";
    }

    if ($bytes >= $kilobyte && $bytes < $megabyte) {
        return round($bytes / $kilobyte, $precision) . " kb";
    }

    return round($bytes / $megabyte, $precision) . " mb";
}

print "memory:" . formatBytes(memory_get_peak_usage());</code>

(サンプルコードにはgenerate.phpが含まれています。これはこれらのCSVファイルを作成するために使用できます...)

大きなCSVファイルがある場合、このコードはこれらの配列をリンクするのにどれだけのメモリが必要かを示す必要があります。 PHPはすべてをメモリに保つ必要があるため、少なくとも読む必要があるファイルと同じサイズです。

ジェネレーターが救助に来ます!

この問題を改善する1つの方法は、ジェネレーターを使用することです。あなたが彼らに慣れていないなら、今はもっと学ぶのに良い時期です。

ジェネレーターを使用すると、少量の合計データを一度にロードできます。発電機であまりする必要はありません:

<code class="language-php">function readCSV($file) {
    $rows = [];

    $handle = fopen($file, "r");

    while (!feof($handle)) {
        $rows[] = fgetcsv($handle);
    }

    fclose($handle);

    return $rows;
}

$authors = array_filter(
    readCSV("authors.csv")
);

$categories = array_filter(
    readCSV("categories.csv")
);

$posts = array_filter(
    readCSV("posts.csv")
);</code>

CSVデータを繰り返すと、必要なメモリの量がすぐに削減されることに気付くでしょう。

<code class="language-php">function filterByColumn($array, $column, $value) {
    return array_filter(
        $array, function($item) use ($column, $value) {
            return $item[$column] == $value;
        }
    );
}

$authors = array_map(function($author) use ($posts) {
    $author["posts"] = filterByColumn(
        $posts, 1, $author[0]
    );

    // 对 $author 进行其他更改

    return $author;
}, $authors);

$categories = array_map(function($category) use ($posts) {
    $category["posts"] = filterByColumn(
        $posts, 2, $category[0]
    );

    // 对 $category 进行其他更改

    return $category;
}, $categories);

$posts = array_map(function($post) use ($authors, $categories) {
    foreach ($authors as $author) {
        if ($author[0] == $post[1]) {
            $post["author"] = $author;
            break;
        }
    }

    foreach ($categories as $category) {
        if ($category[0] == $post[1]) {
            $post["category"] = $category;
            break;
        }
    }

    // 对 $post 进行其他更改

    return $post;
}, $posts);</code>
以前にメモリ使用量のメガバイトを見たことがある場合、Kilobytesが表示されます。これは大きな改善ですが、問題がないわけではありません。

まず、

およびarray_filter発電機では動作しません。このタイプのデータを処理するための他のツールを見つける必要があります。これがあなたが試すことができるツールです! array_map

<code class="language-php">function formatBytes($bytes, $precision = 2) {
    $kilobyte = 1024;
    $megabyte = 1024 * 1024;

    if ($bytes >= 0 && $bytes < $kilobyte) {
        return $bytes . " b";
    }

    if ($bytes >= $kilobyte && $bytes < $megabyte) {
        return round($bytes / $kilobyte, $precision) . " kb";
    }

    return round($bytes / $megabyte, $precision) . " mb";
}

print "memory:" . formatBytes(memory_get_peak_usage());</code>
このライブラリは、イテレーターとジェネレーターで使用できるいくつかの機能を紹介します。では、メモリにデータを保存せずに、どのようにしてこれらすべての関連データを取得しますか?

<code class="language-php">function readCSVGenerator($file) {
    $handle = fopen($file, "r");

    while (!feof($handle)) {
        yield fgetcsv($handle);
    }

    fclose($handle);
}</code>
これはより単純になる可能性があります:

<code class="language-php">foreach (readCSVGenerator("posts.csv") as $post) {
    // 使用 $post 执行某些操作
}

print "memory:" . formatBytes(memory_get_peak_usage());</code>
(各データソースの再読み取りは毎回非効率的です。メモリ内で小さな関連データ(著者やカテゴリなど)を保存することを検討してください...)

その他の興味深いこと ニキッチの図書館の場合、これは氷山の一角にすぎません!アレイ(またはiterator/generator)を平らにしたいと思ったことはありませんか?

<code class="language-bash">composer require nikic/iter</code>

などの関数を使用して、反復変数のスライスを返すことができます。 slice takeジェネレーターをもっと使用する場合、常にそれらを再利用する必要がないことがわかります。次の例を考えてみましょう:

<code class="language-php">// ... (后续代码与原文类似,但使用iter库函数进行优化,此处省略以节省篇幅) ...</code>

コードを実行しようとすると、「閉じた発電機を通過できない」という例外が促されます。このライブラリの各イテレーター関数には、回転可能な対応する関数があります:

<code class="language-php">// ... (使用iter库函数简化代码,此处省略以节省篇幅) ...</code>
このマッピング機能を複数回使用できます。独自のジェネレーターを巻き戻すこともできます:

<code class="language-php">// ... (使用iter\flatten和iter\toArray函数的示例代码,此处省略以节省篇幅) ...</code>
あなたがそれから得るものは再利用可能な発電機です!

<code class="language-php">// ... (使用iter\slice和iter\toArray函数的示例代码,此处省略以节省篇幅) ...</code>
結論

考慮する必要があるループ操作ごとに、ジェネレーターがオプションである場合があります。それらは他のことにも役立ちます。言語機能が不十分な場合、Nikicのライブラリは多数の高次関数を提供します。 すでに発電機を使用していますか?パフォーマンスの改善のために、独自のアプリケーションにそれらを実装する方法について、より多くの例を見たいですか?教えてください!

(FAQSの部分は元のテキストに似ており、スペースを節約するためにここで省略されています。PAQの部分は、必要に応じてオプションで保持または再編成できます。

以上がメモリパフォーマンスは、ジェネレーターとNikic/Iterで向上しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。