2017-10-02 30 views
5

Mam duży zestaw tablic asocjacyjnych. Każdy asocjacyjna składa się z około 15 kluczy różnych typów (ciąg, liczba całkowita, float) - Mniejszy przykład poniżej:array - brak pamięci w php

$array = [ 
    [ 
     "key1" => "string", 
     "key2" => 10, 
     "key3" => 4.05 
    ],  
    [ 
     "key1" => "string2", 
     "key2" => 20, 
     "key3" => 1.05 
    ],  
    ... 
]; 

Teraz chcę iteracyjne nad tej tablicy i dodać kilka klawiszy jak

$map = array_map(function (array $item) {    
     $item['key4'] = 1; 
     $item['key5'] = 1; 
     $item['key6'] = 1; 
     return $item; 
    }, $array); 

Problem: Dla tablicy, która zawiera dość dużą liczbę tablic asocjacyjnych, dodanie nowych kluczy powoduje, że osiągnięto limit pamięci i skrypt zostaje zakończony. Czy masz jakieś rozwiązania?

+8

Protip: zacznij używać obiektów z klasami i wróć do tych z bazą danych, jeśli naprawdę masz tyle danych do pracy, że skończy ci się pamięć. Tak działa pamięć: bufor cpu, jeśli go zabraknie, RAM, jeśli go zabraknie, czas na korzystanie z systemu plików. –

+0

Jeśli masz wystarczająco dużo pamięci, zawsze możesz zwiększyć limit pamięci: - http://php.net/manual/en/ini.core.php#ini.memory-limit – daker

+0

@ Mike'Pomax'Kamermans Dzięki za odpowiedź. Masz na myśli, że powinienem używać wielu obiektów, prawda? – Joe

Odpowiedz

2

Można paginate swoje dane, fragment macierzy do pracy z mniejszych kawałków, a nawet zwiększyć memory_limit, ale załóżmy, że masz dużą tablicę i nie może zrobić inaczej.

Bawmy się więc z macierzą długości 1 000 000 i wypróbuj różne rozwiązania. Włożę zużycia pamięci pomiarów czasowych & obliczeniowych z mojego laptopa

obecne rozwiązanie (857MB/640ms)

for ($i=0; $i< 1000000; $i++){ 
    $array[$i] = [ 
     "key" => 'value', 
     "key2" => $i, 
     "key3" => $i/3 
    ]; 
} 

$map = array_map(function (array $item) { 
    $item['key4'] = 1; 
    $item['key5'] = 1; 
    $item['key6'] = 1; 
    return $item; 
}, $array); 

Z tego kawałka kodu zużycie pamięci na moim laptopie jest 857MB i czas obliczeniowej 640ms.

W tym przykładzie tworzysz zupełnie nową zmienną $map ze swojej $array. Oznacza to, że tworzysz nową kopię tablicy w pamięci.

Praca z references (480MB/220ms)

$array = []; 
for ($i=0; $i< 1000000; $i++){ 
    $array[$i] = [ 
     "key" => 'value', 
     "key2" => $i, 
     "key3" => $i/3 
    ]; 
} 

foreach ($array as &$item) { 
    $item['key4'] = 1; 
    $item['key5'] = 1; 
    $item['key6'] = 1; 
} 

z wykorzystaniem &$item zadajemy PHP dać nam dostęp do zmiennej przez referencję, co oznacza, że ​​mamy do modyfikowania danych bezpośrednio w pamięci bez tworzenia nową kopię tego.

Dlatego właśnie ten skrypt zużywa o wiele mniej pamięci czasu obliczeń.

Praca z klas (223MB/95ms)

Pod maską, PHP używa struktur danych C do zarządzania danymi w pamięci. Klasy są przewidywalne i znacznie łatwiejsze do optymalizacji przez PHP niż tablica. Jest dobrze wyjaśnione here

class TestClass { 
    public $key1, $key2, $key3, $key4, $key5, $key6; 
} 

$array = []; 
for ($i=0; $i< 1000000; $i++){ 
    $array[$i] = new TestClass(); 
    $array[$i]->key1 = 'value'; 
    $array[$i]->key2 = $i; 
    $array[$i]->key3 = $i/3; 
} 

foreach ($array as $item) { 
    $item->key4 = 1; 
    $item->key5 = 1; 
    $item->key6 = 1; 
} 

Można zobaczyć, że zużycie pamięci & czas do iteracji są znacznie niższe. Wynika to z faktu, że PHP nie musi modyfikować struktury danych w pamięci: każde pole obiektu jest gotowe do odbioru danych.

Należy jednak zachować ostrożność, jeśli doda się pole, które nie zostało zadeklarowane w klasie (np. $item->newKey = 1: newKey jest zadeklarowane dynamicznie): optymalizacja pamięci nie będzie już możliwa i nastąpi przejście do 620 MB użycia pamięci & 280ms obliczeniowej)


Jeśli chcesz iść dalej i nie boją się bólu głowy, spojrzeć na Standard PHP Library (SPL): znajdziesz wiele zoptymalizowanych rozwiązań struktur danych (stały tablice, Iteratory & więc na ...)

PS: test porównawczy wykonany z Xdebug wyłączony

-1

Powinieneś być w stanie zaoszczędzić trochę pamięci, jeśli korzystasz z referencji. Spowoduje to usunięcie wielu kopii podczas wykonywania akcji zapisu, które występują w tle. W małym przypadku testowym. Udało mi się zmniejszyć pamięć o 30% -40% (w zależności od wersji PHP). Jeśli używasz PHP5, możesz również skorzystać z aktualizacji do PHP7. Oczywiście nie potrafię przewidzieć, czy jedno z nich, czy oba, zaoszczędzi wystarczającą ilość pamięci. Przypadek Testowy (wystarczy usunąć /* przed mapie lub pieszo):

$cnt=10000; 
for($i=0;$i<$cnt;$i++) { 
    $array[]['key1'] = 1; 
    $array[]['key2'] = 2; 
    $array[]['key3'] = 3; 
} 
/*array_walk($array, function (&$item,$key) {    
    $item['key4'] = 1; 
    $item['key5'] = 1; 
    $item['key6'] = 1; 
}); //memory used PHP7/PHP5: 13 437 720 - 25 924 944 */ 
/*$map = array_map(function (array $item) {    
    $item['key4'] = 1; 
    $item['key5'] = 1; 
    $item['key6'] = 1; 
    return $item; 
}, $array); //memory used PHP7/PHP5: 25 050 360 - 40 850 480*/