Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Sortowanie duzego pliku.
Forum PHP.pl > Forum > PHP
Sokrates
Witam , mam takie pytanie,
mam funkcje ktora przeszukuje plik i wyswietla na ekran
posortowane liczby z tego pliku.
Funkcja wyglada tak:
  1. <?php
  2. function f($min, $max) {
  3. $tab = array();
  4. $plik = fopen ("sort_out.txt", "r");
  5. while (!feof($plik)) {
  6. $xx = fgets ($plik);
  7. if ($xx >= $min && $xx <= $max) $tab[] = $xx;
  8. }
  9. fclose($plik);
  10. sort($tab, SORT_NUMERIC);
  11. foreach ($tab as $x) echo $x."<br />"; 
  12. }
  13. ?>

a wywołanie jej tak:
  1. <?php
  2. for ($i=0; $i < 10000000; $i += 250000) f($i, $i + 250000 - 1);
  3. ?>


Plik moze mieć 9999999 pozycji (wierszy) wiersz nie bedzie wiekrzy niż 7 cyfrowa liczba.

Moj problem, polega na tym ze jesli plik jest maly to sortowanie sie udaje, wszystko dziala ladnie,
ale jak plik jest duzy np 1000950 wierszy cyfr 7 cyfrowych to wyswietla mi taki komunikat:
  1. <?php
  2. Fatal error: Allowed memory size of 8388608 bytes exhausted (tried to allocate 9 bytes) in index.php on line '$xx = fgets ($plik);'.
  3. ?>

Dlaczego cos takiego wyswietla, zakladajac ze 7 cyfrowa liczba (jeden wiersz) miesci sie na 4B (bajtach),
przy jednym wywolaniu petli jest 250000 cyfr co daje okolo 0,5 MB zuzycia pamieci.

Czy ktos moze wyjasniec mi ta sytuacje?
Dzieki,
Pozdrawiam...
l0co
Spróbuj tego

Edit: A tak na moje to robiąc fgets pobierasz string a string 7 znakowy zajmuje 8 bajtów (plus zero kończące, chociaż nie wiem dokładnie jaki jest wewnętrzny format danych PHP). Poza tym Twój kod jest nieoptymalny, powinieneś po pobraniu liczby od razu wstawiać na odpowiednie miejsce w tablicy.
Sokrates
Wprowadzilem drobne zmiany w funkcji a mianowicie zamienilem stringa na inta
  1. <?php
  2. $xx = (int)fgets ($plik);
  3. ?>

Troche to pomoglo, ale po jakims czasie kiedy wyswietlilo okolo 50000 cyfr posortowanych na stronie
wywalolo mi taki sam blad jak poprzednio.
Dopiero po wprowadzniu zmian w petli for
  1. <?php
  2. for ($i=0; $i < 10000000; $i += 50000) f($i, $i + 50000 - 1);
  3. ?>

Wyswietlilo mi posortowany plik na stronie.

Pytanie dlaczego tak sie dzieje?
Ta funkcja sortujaca miala dzialac na 2 MB RAMu.
Dla 7 cyfrowych liczb kiedy jedna cyfra przyjmuje 4B, jest to az 524288 wierszy.

Co do optymalizacji kodu:
Nie moge wczytac calego pliku do pamieci (Tablicy) bo to o wiele przekracza 2MB RAMu,
jesli plik skladal by sie z 9999999 pozycji (a takie jest zalozenie), to wczytanie calego pliku do pamieci (Tablicy)
wymagalo by az 40MB RAM.
l0co
Może to odpowie na Twoje pytanie:

Kod
<?
         $ALEN = 10000;
    
         echo "TEST A";
         $tab = array();
         for ($i = 0; $i < 50000; $i++) {
             $tab[] = (int) (1234567+$i);
             if ($i % $ALEN === 0)
                 echo "--- $i -- " . memory_get_usage();
         }
    
         echo "TEST B";
         $tab = array();
         for ($i = 0; $i < $ALEN; $i++)
             $tab[$i] = 0;
         for ($i = 0; $i < 50000; $i++) {
             $tab[$i % $ALEN] = (int) (1234567+$i);
             if ($i % $ALEN === 0)
                 echo "--- $i -- " . memory_get_usage();
         }        
     ?>



Output:

Kod
TEST A--- 0 -- 42880
     --- 10000 -- 668384
     --- 20000 -- 1293944
     --- 30000 -- 1853944
     --- 40000 -- 2545016
     TEST B--- 0 -- 668320
     --- 10000 -- 668352
     --- 20000 -- 668368
     --- 30000 -- 668384
     --- 40000 -- 668400


Wnioski: dodawanie elemenu do tablicy jest kosztowne i nie ma się co dziwić. Nie wiem jaki jest model pamięci PHP ale generalnie taka operacja wiąże się z ciągłą realokacją pamięci (zobacz jakie liczby są w pierwszym przykładzie) - dodawanie elementu do tablicy w ten sposób to bardzo rozrzutne traktowanie pamięci. Spróbuj zaalokować pamięc przed wykorzystaniem i korzystaj zawsze z tego samego bufora.

PS. Wydaje mi się że robisz jakąś strasznie nieprzemyślaną rzecz - co chcesz konkretnie zrobić? Skąd wiesz jaki jest rozkład statystyczny liczb - tutaj chyba zakładasz że jest stały. Zastanów się, jak będzie działał algorytm jeśli w całym pliku będzie tylko jedna i ta sama liczba?
Sokrates
Cytat(l0co @ 3.09.2007, 18:25:22 ) *
PS. Wydaje mi się że robisz jakąś strasznie nieprzemyślaną rzecz - co chcesz konkretnie zrobić? Skąd wiesz jaki jest rozkład statystyczny liczb - tutaj chyba zakładasz że jest stały. Zastanów się, jak będzie działał algorytm jeśli w całym pliku będzie tylko jedna i ta sama liczba?


Nie bedzie jednej i tej samej liczby w calym pliku. Bo sa to nr. telefonow (7 cyfrowe), ktore sie nie powtarzaja.
Plik jest nie posortowany a nr. telefonow w nim sa porozrzucane. Musze ten plik posortowac przy urzyciu tylko 2MB pamieci RAM i wyswietlic wynik bezposrednio na ekran posortowanych liczb. Nie moge uzyc do tego jakiegos dodatkowego pliku tymczasowego ktory by zwalnial pamiec. Wynik ma sie pojawic bezposrednio na ekranie. Wynik posortowanego pliku ma sie pojawic na ekranie w miare odrazu (parenascie minut - nie parenascie godz.)

Nie rozumiem tego zapisu, mozesz mi wyjasnic po co tam jest % (mod) ?
Cytat(l0co @ 3.09.2007, 18:25:22 ) *
$tab[$i % $ALEN] = (int) (1234567+$i);

Tego tez nie rozumiem, jesli juz deklarujesz predzej tablice z jakimis wartosciami to dla czego tylko dla 10000 elementow:
Cytat(l0co @ 3.09.2007, 18:25:22 ) *
$ALEN = 10000;
for ($i = 0; $i < $ALEN; $i++) $tab[$i] = 0;


Czy mogl bys rozszerzyc swojej wczesniejsze wyjasnienie ?

Dzieki.
l0co
Cytat
Nie bedzie jednej i tej samej liczby w calym pliku. Bo sa to nr. telefonow (7 cyfrowe)

No tak, ale powiedzmy że to w moim okregu numery to zazwyczaj 3xx-ab-cd. To i tak wychodzi że wszystkie znajdą się wyłącznie w jednym przedziale. Rozkład statystyczny numerów telefonów będzie raczej normalny niz stały (jeśli się nie znasz na statystyce - spróbuj tu.

Cytat
Nie rozumiem tego zapisu, mozesz mi wyjasnic po co tam jest % (mod) ?

Mod jest po to, aby używać tego samego bufora dla każdego kolejnego przedziału liczb w zakresie [$i,$i+$ALEN]

Cytat
Tego tez nie rozumiem, jesli juz deklarujesz predzej tablice z jakimis wartosciami to dla czego tylko dla 10000 elementow:

Widzę że nie rozumiesz mojego testu. Deklaruję sobie stałą tablicę ponieważ zawsze już używam wyłącznie jej (bez żadnej dodatkowej allokacji i marnowania pamięci). Przyjąłem sobie po prostu długość bufora 10000 i tyle. BTW: ten sposób tworzenia tablicy, jaki przedstawiłem też jest marnotrawny, powinienem lepiej byc może użyć array_fill - trzeba by zrobić testy.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.