Nikita Popov는 반복자 및 발전기와 함께 사용할 수있는 기능을 소개하는 Nikic/Iter 라이브러리를 생성하여 불필요한 중간 배열 생성을 피함으로써 크게 메모리를 저장합니다.
생성기 및 Nikic/ITER 라이브러리는 대형 CSV 파일로 작업 할 때 특히 유용하며, 이는 한 번에 메모리에 모두로드하지 않고 큰 데이터 세트를 처리 할 수 있습니다.
생성기는 메모리 성능을 크게 향상시킬 수 있지만 및 와 호환되지 않는 것과 같은 일부 고유 한 도전을 제시하며, Nikic/Iter와 같은 다른 도구가 그러한 데이터를 처리하기 위해 필요합니다.
질문
당신이 관계형 데이터가 많고 사전 로딩을하고 싶다고 가정합니다. 데이터가 쉼표로 구분되었을 수 있습니다. 각 데이터 유형을로드하고 함께 그룹화해야합니다.
다음 간단한 코드로 시작할 수 있습니다
-
그런 다음 반복 또는 고차 함수를 통해 관련 요소를 연결하려고 시도 할 수 있습니다.
좋아 보인다? 그렇다면 많은 CSV 파일이 구문 분석 할 때 어떻게됩니까? 메모리 사용량을 조금 분석 해 봅시다 ... -
(샘플 코드에는 <li>가 포함되어 있으며,이 CSV 파일을 생성하는 데 사용할 수 있습니다 ...)
<large> 큰 CSV 파일이있는 경우이 코드는이 배열을 묶는 데 얼마나 많은 메모리가 필요한지 표시해야합니다. PHP는 모든 것을 메모리에 유지해야하기 때문에 읽어야 할 파일과 동일한 크기입니다. </large>
</li>
<li> 발전기가 구출됩니다! <code>array_filter
이 문제를 개선하는 한 가지 방법은 발전기를 사용하는 것입니다. 당신이 그들에게 익숙하지 않다면, 지금은 더 많은 것을 배우기에 좋은시기입니다. array_map
생성기를 사용하면 한 번에 소량의 총 데이터를로드 할 수 있습니다. 발전기와 많은 일을 할 필요가 없습니다 :
CSV 데이터를 반복하면 필요한 메모리의 양이 즉시 줄어들 것임을 알 수 있습니다.
<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>
는 발전기와 작동하지 않습니다. 이러한 유형의 데이터를 처리하기위한 다른 도구를 찾아야합니다. 다음은 시도 할 수있는 도구입니다!
이 라이브러리는 반복자 및 발전기와 함께 사용할 수있는 일부 기능을 소개합니다. 그렇다면 메모리에 데이터를 저장하지 않고 어떻게이 관련 데이터를 모두 얻습니까?
array_filter
이것은 더 간단 할 수 있습니다 :
array_map
(각 데이터 소스를 다시 읽는 것은 매번 비효율적입니다. 메모리에 작은 관련 데이터 (예 : 저자 및 범주)를 저장하는 것을 고려하십시오 ...)
다른 흥미로운 것들
Nikic의 도서관의 경우, 이것은 빙산의 일각 일뿐입니다! 배열 (또는 반복자/생성기)을 평평하게하고 싶었던 적이 있습니까? <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>
결론
고려해야 할 모든 루프 작업의 경우 생성기가 옵션 일 수 있습니다. 그들은 심지어 다른 것들에도 유용합니다. 언어 기능이 불충분 한 경우 Nikic의 라이브러리는 많은 고차 기능을 제공합니다.
이미 발전기를 사용하고 있습니까? 성능 향상을 위해 자신의 응용 프로그램에서 구현하는 방법에 대한 더 많은 예를보고 싶습니까? 제발 알려주세요!
(FAQS 부분은 원래 텍스트와 유사하며 공간을 절약하기 위해 여기서 생략됩니다. FAQ 부품은 필요에 따라 선택적으로 유지되거나 재구성 될 수 있습니다.)