Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Biblioteka fstream i wielkość plików

Ostatnio zmodyfikowano 2017-02-20 22:19
Autor Wiadomość
Elaine
» 2017-02-19 21:41:51
RAID-1 składające się z dwóch HDD:
Operator <<: 3.8633s
Metoda write: 2.19277s
SSD:
Operator <<: 3.84773s
Metoda write: 2.12333s
tmpfs:
Operator <<: 3.83204s
Metoda write: 2.04082s

Wyniki nie mają sensu, bo podają czas procesoraclock używa clock_gettime z CLOCK_PROCESS_CPUTIME_ID! — nie czas zegara ściennego. Po zmianie kodu tak, by korzystał z CLOCK_MONOTONIC:
HDD:
Operator <<: 3866ms
Metoda write: 2029ms
SSD:
Operator <<: 3956ms
Metoda write: 2197ms
tmpfs:
Operator <<: 3847ms
Metoda write: 1919ms

Ha ha ha.

Teza: bottleneckiem jest std::fstream. Co się stanie, jeśli porównać też open + write + fdatasync (chcemy przecież zapisać coś na dysk, a nie tylko skopiować do page cache kernela) + close?
HDD:
Gołe syscalle: 4932ms
SSD:
Gołe syscalle: 1145ms
tmpfs:
Gołe syscalle: 86ms

Daje to kolejno 100, 450 i 6000 MB/s. To już ma sens i pokazuje, że problemy z wydajnością są spowodowane przez std::fstream, co mnie absolutnie nie dziwi.

To, że operator<< jest wolniejszy od funkcji składowej write również mnie nie dziwi, to pierwsze musi dokonać konwersji liczby na postać tekstową, co swoje kosztuje, zwłaszcza w standardowych strumieniach, gdzie trzeba przejść przez std::num_put.
P-158024
michal11
» 2017-02-19 22:12:27
Możesz jeszcze napisać czym kompilowałeś?
P-158025
Elaine
» 2017-02-19 22:20:43
>> gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 6.3.0-7' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 6.3.0 20170218 (Debian 6.3.0-7)

Clang z libc++ daje podobne wyniki, tylko operator<< jest prawie 5× wolniejszy:
Operator <<: 18.6843s
P-158027
j23
» 2017-02-20 13:53:36
Teza: bottleneckiem jest std::fstream. Co się stanie, jeśli porównać też open + write + fdatasync (chcemy przecież zapisać coś na dysk, a nie tylko skopiować do page cache kernela) + close?
Wprawdzie fwrite jest szybsza od fstream::write przy tak skonstruowanym teście, to przy usunięciu jednej pętli (128) na rzecz bufora int[128] różnice w czasach są nieznaczne (co ciekawe u mnie na korzyść fstream::write). Przy zapisie blokami 128 * 1024 i usunięciu jeszcze jednej pętli wychodzi mi, że kod z fstream::write jest około trzy razy szybszy od wersji z fwrite.


Wspomnę jeszcze, że operator<< jest szybszy od fprintf, ale to dziwić nikogo nie powinno.



p.s. testowałem na MinGW 5.1.0.
P-158042
mokrowski
» 2017-02-20 18:39:00
Troszkę te wasze dyskusje o prędkości przypominają udowadnianie że rower jest lepszy od samochodu. Strumienie w C++ to dość złożony podsystem który ma na celu kompleksowe obsłużenie zagadnień z którymi np. printf sobie nie radził lub nie miał nawet w planach radzić. Nie oznacza to że jest szybszy czy wolniejszy... to zależy... proponuję sprawdzić następujące fakty i ew. wyciągać wnioski:
1. Wyłączyć synchronizację z stdio znanego z C. Strumień respektuje mieszane wywołania. Czyli np. część tradycyjnego C (printf i pochodne) i część strumieni. Znaki się nie mieszają bo synchronizacja jest włączona. Wyłączenie:
C/C++
ios_base::sync_with_stdio( false );
2. Strumienie wejściowe mają wbudowaną kontynuację gdzie oczekują na synchronizację z innym. W metodzie strumienia (w tym przypadku otwartym pliku) w metodzie tie, warto podać wskaźnik nullptr. Dość mocno przyśpieszy.
C/C++
openFile.tie( nullptr );
3. Jeśli zapis jest wykonywany przy dużej ilości danych, warto zmienić wielkość bufora:
C/C++
const unsigned int length = 8192;
char buffer[ length ];
std::ifstream openFile;
openFile.rdbuf()->pubsetbuf( buffer, length );
openFile.open( "file.bin", std::ios::binary | std::ios::trunc );
4. Czy zamiast konwersji które wykonuje printf() może być coś szybszego? W większości wypadków (podkreślam że w większość bo są wyjątki) tak. Dostępna jest biblioteka boost::lexical_cast .. odnośnik do wydajności http://www.boost.org/doc/libs/1_62_0/doc/html/boost_lexical_cast/performance.html
5. W trakcie close() na pliku (czyli także w takcie niszczenia obiektu pliku), następuje automatyczna synchronizacja.

Jak teraz będzie pritnf szybsze, super! Sprawdziłeś! Jeśli nie, również super! Wiesz że kilku "gałek i wajch" printf nie ma :-) Więc jeśli ich nie potrzebujesz, to printf będzie ok.
P-158063
j23
» 2017-02-20 19:20:52
@mokrowski, bez przesady. Z takich dyskusji czasami można się czegoś ciekawego dowiedzieć, jak np. to, że strumienie z C++ nie są z definicji wolniejsze od tych z C (dość powszechny mit). Że na szybkość czytania/zapisywania dużej ilości danych większy wpływ ma buforowanie, a nie użyte strumienie czy funkcje systemowe.
P-158064
pekfos
» 2017-02-20 20:50:42
strumienie z C++ nie są z definicji wolniejsze od tych z C (dość powszechny mit).
"Strumienie C++ definiuje się jako wolniejsze odpowiedniki rozwiązań z C.." :D Jednak mit jest, głównie przez to, że osoby nie mające pojęcia o programowaniu biorą się za zadania z limitem czasu, a I/O znają tylko z kursu i w podstawowym zakresie. "Mam problem, przekraczam limit czasu" i odpowiedź to zwykle "użyj printf", bo w takim przypadku tłumaczenie niuansów jest jak rzucanie grochem w ścianę, na której jest wyraźnie napisane "nawet śrut nic nie dał, ale próbuj szczęścia".
P-158066
Elaine
» 2017-02-20 21:38:10
Strumienie w C++ to dość złożony podsystem który ma na celu kompleksowe obsłużenie zagadnień z którymi np. printf sobie nie radził lub nie miał nawet w planach radzić.
Kontrprzykładem jest tutaj i18n, z którym printf radzi sobie lepiej, bo nie rozbija wiadomości na kawałki.
1. Wyłączyć synchronizację z stdio znanego z C.
To ma wpływ tylko na std::{,w}c{in,out,err,log}, a temat jest o strumieniach plikowych.
2. Strumienie wejściowe mają wbudowaną kontynuację gdzie oczekują na synchronizację z innym. W metodzie strumienia (w tym przypadku otwartym pliku) w metodzie tie, warto podać wskaźnik nullptr. Dość mocno przyśpieszy.
Strumienie plikowe nie są domyślnie powiązane z niczym, więc to nic nie zmieni; patrz 27.5.5.2, do którego pośrednio odnosi się 27.9.5.1.
3. Jeśli zapis jest wykonywany przy dużej ilości danych, warto zmienić wielkość bufora
To nic nie da, gdy bottleneckiem jest samo kopiowanie danych do tegoż bufora — profilowałem, najwięcej czasu program malego7 spędza w std::basic_filebuf<char>::xsputn!
4. Czy zamiast konwersji które wykonuje printf() może być coś szybszego? W większości wypadków (podkreślam że w większość bo są wyjątki) tak. Dostępna jest biblioteka boost::lexical_cast
boost::lexical_cast też jest wolne, jeśli komuś zależy na wydajności, to używa Karmykarma best waifu z Boost.Spirit, std::to_chars (które jeszcze nie istnieje) lub customowego kodu.


"Strumienie C++ definiuje się jako wolniejsze odpowiedniki rozwiązań z C.." :D
Najśmieszniejsze jest w tym wszystkim to, że tak definiuje je standard. Nie, nie żartuję, standardowe strumienie plikowe są zdefiniowane jako wrappery wokół FILE*, a konwersje liczb z/do ciągów znaków zdefiniowane są przy użyciu strto{l,ll,ul,ull,f,d,ld} i printf. Implementacje starają się jak mogą realizować to na sposoby szybsze niż wywoływanie funkcji z C, ale nie zawsze się da.
P-158069
1 2 « 3 » 4
Poprzednia strona Strona 3 z 4 Następna strona