Szybki odczyt z pliku...

dział ogólny

Szybki odczyt z pliku...

Nowy postprzez Slynx » poniedziałek, 1 sierpnia 2011, 14:39

Szukałem podobnych tematów, ale nic specjalnego nie znalazłem....

Wróciłem do mojej funkcji generującej CRC32 dla pliku i złapałem się na jednym przy testach (chciałem już przepisywać pod asm) - podczas liczenia obciążenie procesora nie przekracza 60%, tak więc nawet przepisanie tego niewiele by dało. Wąskim gardłem jest odczyt pliku czego się nie spodziewałem (Mam stosunkowo szybki dysk ~70MB /s, więc nie sądziłem, że tak lekko mu przyjdzie przerobić takie ilości danych). Ale wracając do tematu...

Aktualna wersja funkcji korzysta z fread ze standardowych bibliotek (stdlib) co wydawało mi się dobrym rozwiązaniem. Zauważyłem, że ogromne znaczenie ma zastosowana wielkość bufora, gdzie optymalną wielkością okazało się 8 KB (optymalna = najkrótszy czas liczenia). Przy <4KB szybkość odczytu znaczenia spadła. Można coś jeszcze z tym zrobić ? Inny sposób odczytu ? Klasy .Net-owe ? W sumie to nawet nie testowałem, z góry uznałem, że będą wolniejsze. A może po prostu jeszcze o czymś nie wiem.

Zastanawiałem się jeszcze nad jedną opcją. Odczytem danych na jednym wątku i liczeniu na drugim. W ten sposób pętla nie marnowała by czasu na liczenie, a jedynie cały czas odczytywała i wrzucała dane do jakiegoś globalnego bufora, a drugi wątek by je przetwarzał. Jednak z drugiej strony sądzę, że mogłoby to przysporzyć sporo problemów przy kodowaniu :/

Sami mi powiedzcie co o tym myślicie.

Aktualna wersja funkcji (liczenie dla pliku)
KOD cpp:     UKRYJ  
unsigned char* str = new unsigned char[8192];
unsigned long int crc32 = 0xFFFFFFFF;
char *aa = (char*)Runtime::InteropServices::Marshal::StringToHGlobalAnsi(file_path).ToPointer();
int len;
FILE *file = fopen(aa, "rb");
while (( len = fread(str, 1, 8192, file)) > 0)
        for (int i = 0; i < len; i++)
                crc32 = (crc32 >> 8) xor crc_table[(crc32 & 0xFF) xor str[i]]; // crc_table jest tablicą statyczną z wygenerowanymi wszystkim (256) wartościami - dla szybkości.
crc32 = crc32 xor 0xFFFFFFFF;
fclose(file);
return crc32.ToString("X");
 

Wielkiej filozofii tu nie ma.
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: Szybki odczyt z pliku...

Nowy postprzez polymorphism » poniedziałek, 1 sierpnia 2011, 15:40

Może byś powiedział, o jakich rzędach wielkości czasów my tu mówimy? Pytam, bo może nie ma czego optymalizować. Bezsensowna jest walka o 0,5ms/100MB w aplikacji, która nie ma narzuconych jakiś wyśrubowanych rygorów czasowych.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: Szybki odczyt z pliku...

Nowy postprzez Slynx » poniedziałek, 1 sierpnia 2011, 16:02

Aktualnie to jest problem teoretyczny. Nawet te 5 sekund w jedną czy w drugą stronę wiele by zmieniło. Przed chwilą sprawdzałem. Policzenie dla pliku 1 GB zajmuje mu ~18 s. Jak damy do tego pasek postępu to całość będzie się wahać w granicach 20 sekund, a że user widzi, że program się nie zaciął tylko pracuje to nie zrobi mu to różnicy, zwłaszcza, że program (na 99%) nie będzie przetwarzał plików większych niż 1 GB. Sprawdzałem czas dla podobnych programów i też waha się między 15-20 sekund (ciężko dokładnie ustalić). Mówię "problem teoretyczny", ale jeśli ewentualne poprawki będą stosunkowo proste do wprowadzenia to naturalnie, że dorzucę. Czekać te kilka sekund, a nie czekać...
Aktualnie robię pomiary, sprawdzam obciążenie dysku i procesora podczas odczytu. Zaraz sprawdzę jak to wygląda i czy można gdzieś wydusić jakieś sekundy.

I mam jeszcze jedno pytanko. Jak sprawdzić ilość wolnej pamięci ? (Testowo) załadowałem do zmiennej ponad 300 MB danych i "coś" mi po chwili wyczyściło tą zmienną, a program rzucił wyjątkiem jako, że zmienna była już pusta (deklaracja jest globalna). No i jeszcze coś. Wolna pamięć, a plik wymiany. Zamiast rzucać wyjątkiem OutOfMemory nie powinien przypadkiem zacząć zapisywać do pliku wymiany ? Chyba, że tu system ma mało do gadania... No nie wiem, pytam, bo mnie lekko to zdziwiło.

No to wyniki testów mnie lekko zdziwiły. Albo wtedy wyniki miałem błędne, albo na wielkość bufora może mieć wpływ też system (konfiguracja), bo przy tamtych testach miałem jeszcze XP.
Obrazek
Więc ustawiamy na 2 KB....
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: Szybki odczyt z pliku...

Nowy postprzez polymorphism » poniedziałek, 1 sierpnia 2011, 18:11

załadowałem do zmiennej ponad 300 MB danych i "coś" mi po chwili wyczyściło tą zmienną

Nie rozumiem, jak to "coś ci wyczyściło"?

Zamiast rzucać wyjątkiem OutOfMemory nie powinien przypadkiem zacząć zapisywać do pliku wymiany ?

System wie co i kiedy ma zapisywać do pliku wymiany. OutOfMemory możesz dostać wtedy, gdy zużyjesz całą przestrzeń adresową procesu lub ją po prostu pofragmentujesz*), pomimo, że fizycznie wolne miejsce jeszcze jest.

*) problem fragmentacji pamięci w językach zarządzanych jest raczej znikomy.

Więc ustawiamy na 2 KB....

A nie przypadkiem na 1024 KB (~0,4ms/KB)? 2 KB to średnio 40ms/KB, czyli o wiele wolniej. Tu są proste zależności. Większy bufor - mniej odwołań do funkcji fread.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: Szybki odczyt z pliku...

Nowy postprzez Slynx » poniedziałek, 1 sierpnia 2011, 18:57

Nie rozumiem, jak to "coś ci wyczyściło"?


Uwierz mi, że ja też nie rozumiem. Ustawiłem deklarację zmiennej jako globalną, w jednej funkcji wczytałem ten plik (300MB), a gdy kliknąłem na drugą by z niej skorzystała to już danych nie było. Na liczniku (gadżet Win7) zauważyłem tylko skok w pamięci (było obciążenie 84%, czyli odczytał cały plik, ale zanim wyświetlił mi komunikat, że wczytał z powrotem wskoczył na obciążenie 74%, czyli tak jakby zwolnił pamięć).

Wpisałem to jeszcze raz. Dla tego samego pliku. Działa :oops:

System wie co i kiedy ma zapisywać do pliku wymiany. OutOfMemory możesz dostać wtedy, gdy zużyjesz całą przestrzeń adresową procesu lub ją po prostu pofragmentujesz*), pomimo, że fizycznie wolne miejsce jeszcze jest.


Już wiem czemu rzucił tym wyjątkiem. Po prostu funkcja ReadAllBytes nie obsługuje tak dużych plików. Gdybym to odczytywał normalnie w pętli po kawałkach to pewnie wszystko by poszło jak należy.

A nie przypadkiem na 1024 KB (~0,4ms/KB)? 2 KB to średnio 40ms/KB, czyli o wiele wolniej. Tu są proste zależności. Większy bufor - mniej odwołań do funkcji fread.


No chyba nie do końca, bo procesy liczenia i odczytu danych nie są wykonywane asynchronicznie, ale w pętli na przemian, tak więc gdy każesz fread odczytać 1 Mb danych liczenie czeka na zakończenie odczytu (napełnienie bufora), a potem odczyt czeka na zakończenie liczenia, itd. natomiast gdy odczytujesz dane małymi fragmentami to obydwa procesy praktycznie cały czas pracują (nie zapomnijmy o tym, że system też potrzebuje dostępu do dysku i procesora, tak więc liczne przerwy między odczytem, a liczeniem korzystnie wpływają na dostęp systemu do dysku i procesora). Przynajmniej tak mi się wydaje - no a wyniki testów to potwierdziły.
Jednak chyba wiem o co Tobie chodzi, za słabo podpisałem ; p
Podane czasy nie są czasami odczytu bloku danych, a czasem liczenia CRC dla całego pliku przy konkretnej wielkości bufora. (Bufor 2 KB - liczenie CRC ~50 ms ; Bufor 1024 KB - liczenie CRC ~400)
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: Szybki odczyt z pliku...

Nowy postprzez polymorphism » poniedziałek, 1 sierpnia 2011, 20:03

Podane czasy nie są czasami odczytu bloku danych, a czasem liczenia CRC dla całego pliku przy konkretnej wielkości bufora.

Aha, rozumiem. Choć to dość dziwne czasy. Skąd ten drastyczny spadek wydajności przy 4KB? Z reguły bywa tak, że im większy bufor (w granicach rozsądku), tym większa wydajność. Tutaj z kolei nie ma to zastosowania, a różnice czasowe są podejrzanie duże.

Mam nadzieję, że mierzyłeś czasy wersji release...
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: Szybki odczyt z pliku...

Nowy postprzez Slynx » poniedziałek, 1 sierpnia 2011, 20:24

Dużo czynników wpływa na takie pomiary, ciężko jest je poprawnie przeprowadzić, zwłaszcza, że wartości powinny być bardzo dokładne.

Taa.... teraz najlepszy wynik dał bufor 256 KB. Tak czy inaczej, rzeczywista optymalna wartość będzie zależna od komputera, szybkości dysku, pamięci RAM i CPU, tak więc czy teraz mi wyjdzie 2 KB czy 256KB to tak na prawdę nie ma znaczenia. Chyba powinienem zrobić coś w stylu takiego testu, który byłby przeprowadzany przy pierwszym uruchomieniu by ustalić optymalną wartość dla danego komputera. To chyba było by najlepsze rozwiązanie.
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome


  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Ogólne problemy z programowaniem

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 8 gości

cron