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 procesora — clock 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. |
|
michal11 |
» 2017-02-19 22:12:27 Możesz jeszcze napisać czym kompilowałeś? |
|
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 |
|
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. |
|
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: 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. 3. Jeśli zapis jest wykonywany przy dużej ilości danych, warto zmienić wielkość bufora: 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. |
|
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. |
|
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". |
|
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 Karmy karma 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. |
|
1 2 « 3 » 4 |