Rumah >pembangunan bahagian belakang >tutorial php >Prestasi memori meningkat dengan penjana dan nikic/iter

Prestasi memori meningkat dengan penjana dan nikic/iter

Joseph Gordon-Levitt
Joseph Gordon-Levittasal
2025-02-16 09:17:10367semak imbas

php iterator dan penjana: alat yang berkuasa untuk pemprosesan yang cekap set data besar

array dan lelaran adalah asas kepada sebarang permohonan. Apabila kita mendapat alat baru, cara kita menggunakan tatasusunan juga perlu bertambah baik.

Sebagai contoh, penjana adalah alat baru. Pada mulanya kita hanya mempunyai tatasusunan, dan kemudian kita mendapat keupayaan untuk menentukan struktur array kelas kita sendiri (dipanggil iTerators). Tetapi sejak Php 5.5, kita dapat dengan cepat membuat struktur iterator kelas yang dipanggil penjana.

Memory Performance Boosts with Generators and Nikic/Iter

Penjana kelihatan seperti fungsi, tetapi kita boleh menggunakannya sebagai iterators. Mereka memberi kami sintaks mudah untuk mewujudkan fungsi yang boleh diganggu dan berulang. Mereka luar biasa!

Kami akan melihat beberapa bidang di mana penjana boleh digunakan dan meneroka beberapa isu yang perlu diberi perhatian apabila menggunakan penjana. Akhirnya, kita akan belajar perpustakaan hebat yang dicipta oleh Nikita Popov yang berbakat.

Kod sampel boleh didapati di https://github.com/sitePoint-editors/generators-and-iter.

mata utama

    Penjana (tersedia sejak Php 5.5) adalah alat yang berkuasa untuk membuat iterators yang membolehkan penciptaan fungsi yang boleh diganggu, berulang, memudahkan pemprosesan dataset besar dan meningkatkan prestasi memori.
  • Nikita Popov mencipta perpustakaan Nikic/iter yang memperkenalkan fungsi yang boleh digunakan dengan iterator dan penjana, menjimatkan ingatan dengan ketara dengan mengelakkan membuat tatasusunan perantaraan yang tidak perlu.
  • Perpustakaan Penjana dan Nikic/ITER sangat berguna apabila bekerja dengan fail CSV yang besar, yang boleh mengendalikan set data yang besar tanpa memuatkan semuanya ke dalam memori sekaligus.
  • Walaupun penjana dapat meningkatkan prestasi memori dengan ketara, mereka juga mengemukakan beberapa cabaran mereka sendiri, seperti tidak serasi dengan
  • dan array_filter, yang memerlukan alat lain seperti Nikic/ITER untuk mengendalikan data tersebut. array_map

Soalan

Katakan anda mempunyai banyak data relasi dan ingin melakukan beberapa preloading. Mungkin data yang dipisahkan koma, anda perlu memuatkan setiap jenis data dan mengumpulkannya bersama-sama.

anda boleh memulakan dengan kod mudah berikut:

<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>
anda kemudian boleh cuba untuk menggabungkan unsur -unsur yang berkaitan dengan fungsi berulang atau lebih tinggi:

<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>
kelihatan baik, bukan? Jadi, apa yang berlaku apabila kita mempunyai sejumlah besar fail CSV untuk menghuraikan? Mari kita menganalisis penggunaan memori sedikit ...

<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>
(kod sampel mengandungi

, yang boleh anda gunakan untuk membuat fail CSV ini ...) generate.php

Jika anda mempunyai fail CSV yang besar, kod ini harus menunjukkan berapa banyak memori yang diperlukan untuk menghubungkan array ini bersama -sama. Sekurang -kurangnya saiz yang sama seperti fail yang perlu anda baca, kerana PHP perlu menyimpan segala -galanya dalam ingatan.

Penjana datang untuk menyelamatkan!

Satu cara untuk memperbaiki masalah ini ialah menggunakan penjana. Jika anda tidak biasa dengan mereka, kini adalah masa yang baik untuk mengetahui lebih lanjut.

Penjana membolehkan anda memuatkan sejumlah kecil jumlah data sekaligus. Anda tidak perlu berbuat banyak dengan penjana:

<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>

Jika anda melangkah melalui data CSV, anda akan melihat bahawa jumlah memori yang diperlukan akan dikurangkan dengan segera:

<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>

Jika anda telah melihat megabytes penggunaan memori sebelum ini, anda kini akan melihat kilobytes. Ini adalah peningkatan yang besar, tetapi bukan tanpa masalahnya.

Pertama sekali, array_filter dan array_map tidak berfungsi dengan penjana. Anda mesti mencari alat lain untuk memproses jenis data ini. Berikut adalah alat yang boleh anda cuba!

<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>

Perpustakaan ini memperkenalkan beberapa fungsi yang boleh digunakan dengan iterator dan penjana. Jadi bagaimana anda masih mendapat semua data yang relevan ini tanpa menyimpan sebarang data dalam ingatan?

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

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

    fclose($handle);
}</code>

ini lebih mudah:

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

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

(membaca semula setiap sumber data tidak cekap setiap masa. Pertimbangkan menyimpan data berkaitan yang lebih kecil (seperti penulis dan kategori) dalam ingatan ...)

Perkara -perkara menarik lain

Untuk perpustakaan Nikic, ini hanyalah hujung gunung es! Pernah mahu meratakan array (atau iterator/penjana)?

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

anda boleh menggunakan fungsi seperti slice dan take untuk mengembalikan kepingan pembolehubah yang boleh diperolehi:

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

Apabila anda menggunakan penjana lebih banyak, anda mungkin mendapati bahawa anda tidak perlu menggunakannya semula. Pertimbangkan contoh berikut:

<code class="language-php">// ... (使用iter库函数简化代码,此处省略以节省篇幅) ...</code>

Jika anda cuba menjalankan kod, anda akan melihat pengecualian yang mendorong: "tidak boleh melintasi penjana tertutup". Setiap fungsi iterator di perpustakaan ini mempunyai fungsi yang sepadan dengan swappable:

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

Anda boleh menggunakan fungsi pemetaan ini beberapa kali. Anda juga boleh membuat penjana anda sendiri boleh dikembalikan:

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

Apa yang anda dapat dari itu adalah penjana yang boleh diguna semula!

Kesimpulan

Untuk setiap operasi gelung yang perlu anda pertimbangkan, penjana mungkin menjadi pilihan. Mereka juga berguna untuk perkara lain. Di mana ciri bahasa tidak mencukupi, Perpustakaan Nikic menyediakan sejumlah besar fungsi pesanan yang lebih tinggi.

adakah anda sudah menggunakan penjana? Adakah anda ingin melihat lebih banyak contoh tentang cara melaksanakannya dalam permohonan anda sendiri untuk beberapa peningkatan prestasi? Tolong beritahu kami!

(bahagian Soalan Lazim adalah serupa dengan teks asal, dan ditinggalkan di sini untuk menjimatkan ruang. Bahagian FAQ boleh dikekalkan secara pilihan atau disusun semula seperti yang diperlukan.)

Atas ialah kandungan terperinci Prestasi memori meningkat dengan penjana dan nikic/iter. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn