Tối ưu hóa bộ nhớ đối với mảng lớn trong PHP
Giới thiệu
Đôi khi chúng ta rơi vào tình huống phải xử lý các mảng lớn và phải đau đầu ra để tránh rơi vào tình trạng tràn bộ nhớ. Vì vậy, trong bài viết này, chúng ta sẽ xem sức mạnh của câu lệnh yield
trong PHP để giải quyết các vấn đề như thế này.
Câu lệnh PHP Yield là gì?
Trong nhiều năm kinh nghiệm sử dụng PHP, tôi thấy câu lệnh yield
không được sử dụng nhiều. Không biết là do nhiều người không biết đến nó hay sao nữa. Nhưng yield
cho phép chúng ta return
mà không cần cắt quá trình và điều này có thể được sử dụng trong nhiều trường hợp mà việc lặp qua một mảng có thể mất nhiều thời gian.
Hãy xem nó trong một ví dụ rất đơn giản. Giả sử chúng ta có một hàm tạo data mà sau đó chúng ta cần xử lý, (in ra trong trường hợp này):
function generateData(): array
{
$data = [];
for ($i = 1; $i <= 3; $i++) {
$data[] = $i;
}
return $data;
}
$data = generateData();
foreach ($data as $value) {
echo $value;
}
// 1 2 3
Trong trường hợp này, chúng ta đang tạo data, đưa nó vào một mảng, sau đó trả lại để sử dụng.
Chúng ta biết rằng mỗi khi thêm phần tử vào một mảng là bộ nhớ bị chiếm dụng trong máy tính và điều này có thể rất tốn kém memory. Vì vậy, sẽ tốt hơn nếu xử lý từng phần tử khi nó đang được khởi tạo và không đợi đến lúc cuối cùng mảng load xong?
Vì vậy, đây là lúc mà lệnh yield
phát huy tác dụng .
Thực hiện lệnh yield
Từ khóa này cho phép chúng ta "trả về" từng phần tử được lặp lại. Và tại sao tôi lại đặt nó trong dấu ngoặc kép? Bởi vì mặc dù phần tử được trả về nhưng hàm vẫn chưa kết thúc. Thay vào đó, nó đợi vòng lặp quay trở lại để trả về phần tử tiếp theo.
function generateData(): array
{
for ($i = 1; $i <= 3; $i++) {
yield $i;
}
}
$data = generateData();
foreach ($data as $value) {
echo $value;
}
// 1 2 3
Như bạn thấy, không nhất thiết phải lưu từng phần tử trong một mảng rồi trả về. Nếu không, mỗi khi foreach lặp lại $data
, hệ thống sẽ trả về phần data tiếp theo.
Làm thế nào để lệnh yield có lợi cho chúng ta?
Hãy tưởng tượng bạn muốn xử lý một file csv chứa nhiều bản ghi sản phẩm. Và bạn cần đọc nó để tạo báo cáo.
Ví dụ, chúng ta muốn biết những sản phẩm mà chúng ta chưa tặng trong tháng này. Vì vậy, chúng ta đọc một file có chứa các sản phẩm được bán trong tháng này và lọc chúng theo những sản phẩm có giá.
function readFile($file)
{
$productNoFree = [];
while (($data = fgetcsv($file, 1000, ',')) !== false) {
if ($data['price'] > 0) {
$productNoFree[] = $data;
}
}
return $productNoFree;
}
$file = fopen('products.csv', 'r');
$productNoFree = readFile($file);
fclose($file);
if ($productNoFree) {
foreach ($productNoFree as $product) {
echo $product . '\n';
}
}
Không nghi ngờ gì nữa, quá trình này có thể xảy ra tình trạng tràn bộ nhớ vì chúng ta không biết có bao nhiêu sản phẩm có thể có trong kho.
Cách an toàn và tối ưu nhất để làm điều này là sử dụng yield
như sau:
function readFile($file)
{
$productNoFree = [];
while (($data = fgetcsv($file, 1000, ',')) !== false) {
if ($data['price'] > 0) {
yield $data;
}
}
return $productNoFree;
}
$file = fopen('products.csv', 'r');
$productNoFree = readFile($file);
fclose($file);
if ($productNoFree) {
foreach ($productNoFree as $product) {
echo $product . '\n';
}
}
Thử nghiệm với một file khá lớn, đây là kết quả:
không có yield | sử dụng yield | |
---|---|---|
Bộ nhớ tiêu thụ | 20,43Mb | 0,21Mb |
Thời gian thực hiện | 0,083 giây | 0,113 giây |
Như bạn có thể thấy, sử dụng từ khóa yield, quá trình này mất nhiều thời gian hơn nhưng tiêu thụ bộ nhớ rất thấp. Điều này sẽ tránh việc chúng ta phải sửa đổi tham số memory_limit
của php trong thời gian chạy hoặc update file php.ini.
Nhược điểm của yield
Một trong những nhược điểm, như chúng ta vừa thấy, là quá trình này mất nhiều thời gian hơn. Vì vậy, chúng ta nên sử dụng nó đúng lúc.
Một nhược điểm khác về yield là chúng ta không thể return một mảng mà chúng ta đã duyệt qua.
Trong ví dụ cuối cùng, chúng ta không thể lặp qua biến $productsNoFree
sau foreach. Vì, các phần tử không được lưu trữ trong bộ nhớ.
Kết luận
Bạn nên tìm hiểu thêm về câu lệnh yield vì nó mạnh hơn nhiều so với các ví dụ đơn giản mà chúng ta đã thấy ở đây. Tôi hy vọng bạn thích bài viết này và chia sẻ nó nếu bạn đã làm. Hẹn gặp lại các bạn vào những lần sau.