• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Która wersja cout jest najszybsza

Object Storage Arubacloud
0 głosów
505 wizyt
pytanie zadane 10 grudnia 2022 w C i C++ przez zig Nowicjusz (160 p.)

Która wersja cout jest najszybsza? Sprawdzałem na poniższej stronie i z różnymi kompilatorami i ich wersjami pokazuje różne wyniki. 

https://en.cppreference.com/w/cpp/io/manip/endl

std::cout << "Hello " << std::endl;
std::cout << "Hello " << '\n';
std::cout << "Hello " << "\n";
std::cout << "Hello \n";

cout << "Hello " << endl;
cout << "Hello " << '\n';
cout << "Hello " << "\n";
cout << "Hello \n";

Mam jeszcze pytanie jak sprawdzić, którego standardu C++ używa mój kompilator. Ten program poniżej nie pokazuje poprawnie. Wiem że trzeba kompilować z opcją gcc c++=23, c++=2a, c++=2b main.cpp -o main

#include<iostream>

int main()
{
    std::cout << "gcc (GCC) " << __VERSION__ << std::endl;
    if (__cplusplus == 202207L) std::cout << "C++23\n";
    else if (__cplusplus == 202002L) std::cout << "C++20\n";
    else if (__cplusplus == 201703L) std::cout << "C++17\n";
    else if (__cplusplus == 201402L) std::cout << "C++14\n";
    else if (__cplusplus == 201103L) std::cout << "C++11\n";
    else if (__cplusplus == 199711L) std::cout << "C++98\n";
    else std::cout << "pre-standard C++\n";
    printf("%li\n", __cplusplus);
}

 

4 odpowiedzi

+2 głosów
odpowiedź 11 grudnia 2022 przez mokrowski Mędrzec (155,460 p.)
Porównywanie szybkości działania strumieni I/O dla tak małych ilości danych, nie ma żadnego sensu. Jeśli chcesz mieć szybkie wyjście dla takich danych, używaj printf(....) ( <cstdio> ). Miej świadomość że std::endl , jest bardzo ciężkie w działaniu. Po wyprowadzeniu znaku nowej linii, opróżnia bufor. Stąd lepiej go unikać i używać '\n'.

Co do numeru standardu, tu masz kompleksową odpowiedź. Zwracam uwagę że w kompilatorach (szczególnie MS), są nieregularności: https://stackoverflow.com/questions/2324658/how-to-determine-the-version-of-the-c-standard-used-by-the-compiler

Poza tym... świat się zmienia po włączeniu optymalizacji :)
+1 głos
odpowiedź 10 grudnia 2022 przez Wiciorny Ekspert (270,190 p.)

Bez znaczenia ... wszystkie działają z takim samem czasem "zależnym" od zmiennej (typu, wartości) jakiej wypisujesz i ogólnie tego co wypisujesz i w jakim formacie. 
Różnice mogą się pojawić w np. między cout a printf itd.

Dodanie przestrzeni nazw nie wpływa i nic w funkcji nie zmienia, to tylko przestrzeń nazw. 
 

Aby ułatwić jeszcze bardziej tworzenie w C++ dużych, pisanych przez wielu programistów aplikacji, dodano do języka mechanizm przestrzeni nazw (ang. name space). Jest to narzędzie pozwalające logicznie grupować nazwy obiektów rozmaitego typu: zmiennych, funkcji, klas, wyliczeń, wzorców itd.

https://www.tutorialspoint.com/how-to-determine-the-version-of-the-cplusplus-standard-used-by-the-compiler 

komentarz 10 grudnia 2022 przez zig Nowicjusz (160 p.)

Nie wiem czy zajrzałeś na te stronę co podałem, ale nie działają tak samo.

Mam najnowszy kompilator kompatybilny ze standardem C++ 23, ale nadal jest błąd przy tym kodzie.

#include <print>
 
int main()
{
    // Each call to std::println ends with new-line
    std::println("Please");
    std::println("enter");
 
    std::print("pass");
    std::print("word");
 
    std::println(""); // same effect as std::print("\n");
}

 

komentarz 10 grudnia 2022 przez Wiciorny Ekspert (270,190 p.)

przeczytaj dokładnie ... 

if the spawned process performs any screen I/O. In most other usual interactive I/O scenarios, std::endl is redundant when used with std::cout because any input from std::cin, output to std::cerr, or program termination forces a call to std::cout.flush(). Use of std::endl in place of '\n', encouraged by some sources, may significantly degrade output performance.

With \n instead of endl, the output would be the same, but may not appear in real time. 
Tu nie masz nigdzie porównania "przestrzeni nazw" do wystąpienia funkcji bez przestrzeni nazw... więc cout- to dalej cout 

komentarz 10 grudnia 2022 przez zig Nowicjusz (160 p.)
Zastąp końcówkę tego kodu std::endl na tej stronie co podałem tymi innymi wersjami i zobaczysz że pokazuje inne wyniki w milisekundach. Kiedyś czytałem że drobne różnice są i w profesjonalnych programach powinno się używać std:: w kodzie.

https://www.youtube.com/watch?v=etQX4Mme2f4
komentarz 10 grudnia 2022 przez Great Stary wyjadacz (12,360 p.)

Żaden istniejący kompilator nie zaimplementował standardu C++23 w 100%. Postęp możesz zobaczyć tu: https://en.cppreference.com/w/cpp/23

komentarz 10 grudnia 2022 przez zig Nowicjusz (160 p.)
edycja 10 grudnia 2022 przez zig
Czy istnieje kompilator do C++ który nie wspiera zwykłego C i kompiluje tylko najnowsze standardy od C++11 do C++23? Do C istnieje kompilator który nie wspiera języka C++.

Dlaczego przy moich postach ciągle pisze na czerwono 1 zgłoszenie?
komentarz 11 grudnia 2022 przez mokrowski Mędrzec (155,460 p.)

@Great, ba... nawet C++11 także nie :) https://en.cppreference.com/w/cpp/11 (Garbage Collection... )

komentarz 11 grudnia 2022 przez Great Stary wyjadacz (12,360 p.)

@mokrowski to nie jest to samo wsparcie GC co zostało niedawno usunięte w C++23? 

komentarz 11 grudnia 2022 przez zig Nowicjusz (160 p.)
edycja 12 grudnia 2022 przez zig
komentarz 11 grudnia 2022 przez zig Nowicjusz (160 p.)
edycja 12 grudnia 2022 przez zig
Który inny język do gier AAA?
komentarz 11 grudnia 2022 przez mokrowski Mędrzec (155,460 p.)

@zig, W mojej ocenie Rust jest dość czytelny. Języki systemowe oprócz pracy na poziomie domeny rozwiązania, poziomie meta, pracują także na bardzo niskim poziomie gdzie typowanie się zaciera i jakoś należy to odzwierciedlić w czytelnej składni. Większość wymagań dla systemów RTOS, także eliminuje obsługiwanie GC bo to czyni język niedeterministycznym. Stąd też, jakąkolwiek składnię fajną/ładną by języki nie miały, jeśli jest GC, to odpada w przedbiegach jako systemowy ale zyskuje na czytelności.

Osobiście także nie przychylam się do oceny języka tylko ze względu na czytelność. 

0 głosów
odpowiedź 10 grudnia 2022 przez Gynvael Coldwind Nałogowiec (27,530 p.)

Generalnie w zależności od kompilatora wersja z std::cout << "Hello \n"; (z/bez std:: nie robi różnicy) będzie najszybsza*, ale to de facto zależy bardzo od systemu, środowiska w którym uruchamiamy program, i tego jak bardzo kompilator optymalizuje. To powiedziawszy, mierzenie czasu wypisywania samego Hello jest ciekawe w zasadzie tylko w rozumieniu akademickim, bo w praktyce jeśli chce się wypisać dużo danych, to... wpadamy do króliczej dziury pełnej ciekawych problemów ;). A jeśli chcemy wypisać mało danych raz na jakiś czas to to jest bez większego znaczenia.

Natomiast czemu napisałem, że ta wersja będzie najszybsza, a potem i tak powiedziałem "to zależy". Na początek rozważmy każdy z przypadków (pomijając z/bez std::, bo to znika w momencie kompilacji):

 

cout << "Hello " << endl;

W pierwszym przypadku dzieją się 3 rzeczy:

  1. Najpierw wywoływany jest operator<< na obiekcie cout z podaną tablicą znaków "Hello ". To z kolei powoduje dodanie tej sekwencji do wew. bufora danych.
  2. Następnie wywołany jest operator<< na obiekcie cout z podanym wskaźnikiem na funkcję endl (taak, endl to jest funkcja, nie ciąg znaków).
  3. W następstwie tego wywołana zostaje funkcja endl, która powoduje dopisanie znaku końca linii do wewnętrznego bufora, a następnie flush, czyli wypisanie danych znajdujących się w wewnętrznym buforze.

 

cout << "Hello " << '\n';

W drugim przypadku dzieją się 2 rzeczy (lub 1 lub 3 rzeczy, ale do tego wrócę później):

  1. Pierwszy punkt jest identyczny z tym z pierwszego przypadku.
  2. W drugim kroku wywoływany jest operator<< na obiekcie cout z podaną liczbą typu char (czyli de facto znakiem końca linii). Ta liczba/znak będzie w rejestrze/na stosie (w cache), więc dostęp do niej będzie natychmiastowy.

Można tutaj już zauważyć, że nie było flusha. Ale do tego wrócę za chwilę, bo to jest trochę bardziej skomplikowane.

 

cout << "Hello " << "\n";

Trzeci przypadek jest prawie identyczny z drugim, z tą różnicą, że w przypadku drugiego wywołania funkcja operator<< dostanie adres ciągu. Ten ciąg może, ale nie musi być w pamięci podręcznej procesora. Osobiście stawiam, że będzie, bo  w najgorszym wypadku CPU przed chwilą musiał z RAMu i tak ściągać "Hello ", a kompilator pewnie obie te rzeczy wrzucił koło siebie (a więc w ten sam cache-line).

 

cout << "Hello \n";

Czwarty przypadek to tylko jedno wywołanie, bez flush, więc z definicji powinno być najszybsze.

 

Póki co całkiem prosto - na papierze najszybszy jest czwarty wariant (1 wywołanie), potem drugi wariant (2 wywołania, ale drugie ma parametr w rejestrze), potem trzeci (2 wywołania, ale drugie ma parametr w pamięci), ostatecznie pierwszy (3 wywołania + flush).

Teraz do tego wszystkiego dodajmy ten cały "flush", czyli faktycznie przesłanie rzeczy z wewnętrznego bufora na standardowe wejście.

W praktyce jest tak:

  • endl zawsze bezwzględnie spowoduje flush.
  • W implementacjach biblioteki standardowej pod Windowsem Microsoft lubił robić flush trochę częściej, np. przy każdym wywołaniu printf. Natomiast tutaj to nie powinno mieć miejsca.
  • Jeśli program jest uruchomiony ze standardowym wyjściem połączonym z terminalem, to zazwyczaj wewnętrzny bufor jest tzw trybie "buforowania linii", co przekłada się na to, że za każdym razem jak w buforze pojawi się \n, to bufor zostanie zflushowany. A więc flush będzie miał miejsce we wszystkich 4 przypadkach (co nadal uczyni czwarty przypadek najszybszym, bo mniej do roboty ogólnie).
  • Jeśli natomiast program jest uruchomiony ze standardowym wyjściem podpiętym do czegokolwiek innego (potok, socket, /dev/null, etc), to bufor wewnętrzny jest w trybie buforowania aż do uzbierania ileś tam danych (np. 4096 bajtów), i flush nastąpi dopiero wtedy. W tym przypadku tylko przykład z endl zrobi flush (więc będzie najwolniejszy, bo cała reszta flusha po prostu w ogóle nie zrobi).

I teraz dochodzimy do ostatniej ważnej zmiennej, czyli tego co zrobi kompilator. Generalnie w C/C++ jest zasada "as-if", która mówi że kompilator może sobie dowolnie przekształcić kod tak długo jak ostateczny efekt będzie taki sam. I znając kompilatory, stawiam że przerobią przypadki 2 oraz 3 na przypadek 4 (tj. dodadzą \n do stringa), co de facto zrówna je wszystkie w szybkości działania.

(btw jeśli lubicie takie rozkminy, to zachęcam do rzucenia okiem na artykuł, który kiedyś ze znajomymi napisałem o tym jak działa hello world od podszewki - https://gynvael.coldwind.pl/?id=754)

komentarz 10 grudnia 2022 przez zig Nowicjusz (160 p.)
edycja 12 grudnia 2022 przez zig
Dzięki za ciekawą odpowiedź.
komentarz 12 grudnia 2022 przez Oscar Nałogowiec (29,320 p.)
edycja 12 grudnia 2022 przez Oscar

Pomiary szybkości wypisywania przez cout nie jest użyteczny, bo to zleży od wielu czynników, z których napisany kod jest najmniej ważny. To jest operacja we/wy, głowną robote wykonuje system operacyjny - jak koledzy wcześniej zauważyli występuje tam zabawa z buforami i ewentualne wywołanie flush(),  który to jest właściwym wyprowadzeniem danych przez wywołanie funkcji systemu operacyjnego. A to może trwać bardzo różnie, bo wyjście programu może być różnie zorganizowane - ekran w trybie teksowym (np. linux bez powłoki graficznej), w trybie graficznym, wyjście po sieci (sesja ssh), przekierowanie do pliku, wysłanie łaczem szeregowym (albo stare mainframe, albo malutkie komputerki typu atmega) a nawet przekierowany na /dev/null.

Jeśli już mierzyć szybkość procedur bibliotecznych to lepiej użyć streama do pamięci (lub stringa) - ostringstream lub podobnych - które calą swoją robotę wykonują w pamięci w ramach biblioteki i mało (lub wcale) nie korzystają z systemu. Przecież całe formatowanie jest niezależne od rodzaju strumienia, a fizyczne transfery I/O realizują klasy dedykonane to konkretnych "urządzeń" (konsola, plik itp). Klasą bazową wych fizycznych transferów była (o ile pamiętam) klasa streambuf.

komentarz 24 marca 2023 przez rosom Nowicjusz (100 p.)
edycja 24 marca 2023 przez rosom

@Gynvael Coldwind, Której wersji powinno się używać w C++20 #include <format> czy #include "fmt/format.h"?

Mam taki kod 

// #include <fmt/core.h>
// #include <fmt/chrono.h>
// #include <fmt/format>
// #include <format.h>

// #include <print>
// #include <fmt/print>

#define FMT_HEADER_ONLY
#include "fmt/format.h"

// using std::format;
// using std::fmt;
// using fmt::format;

using namespace std;
using namespace fmt::format;

int main()
{
    // fmt::print("Hello, world!\n");
    print("Hello, world!\n");
    // string s = fmt::format("{0}{1}{0}", "abra", "cad");
    string s = format("{0}{1}{0}", "abra", "cad");
    // string s = format("{0}{1}{0}", "abra", "cad");
    // auto s = format("{0}{1}{0}", "abra", "cad");
    cout << s << endl;
    // return 0;

    // std::println("Please");
    // std::println("enter");
 
    // std::print("pass");
    // std::print("word");
 
    // std::println(""); // same effec
}

Jednak fmt::format nie działa, dopiero jak doinstaluję pakiet libfmt-dev i dodam #define FMT_HEADER_ONLY #include "fmt/format.h" dopiero wtedy mogę korzystać z print("{}\n"); ale nadal nie działa println i format. Mam najnowszy GCC 12.2 czy to będzie dodane dopiero w GCC 13?

Czy ten gość w tutorialu do C++20 zainstalował fmt::format z osobnych bibliotek?

https://www.youtube.com/watch?v=SK7JhpFk480&list=PLpQIfX0Z22sYWKtKjvlBJaMqbckZNiqqe

https://github.com/fmtlib/fmt

komentarz 24 marca 2023 przez rosom Nowicjusz (100 p.)

@Gynvael Coldwind, Taki kod mi działa pod GCC 12.2 z opcją -std=c++23 nie musiałem nawet dodawać -lfmt. Teraz pytanie czy GCC 13 doda również fmt::format i fmt::println, czy to już mi nie jest potrzebne ponieważ mogę używać print("\n"); ? Czy fmt::format jest w czymś lepszy od fmt::print, print? Kod wydaje mi się czytelniejszy i nie muszę korzystać z using std::cout; , using namespace fmt; , using namespace std; Oraz najważniejsze pytanie czy GCC 13 sprawi że nie będę już musiał dodawać #define FMT_HEADER_ONLY
#include "fmt/format.h" ponieważ będzie to już wbudowane w kompilator?

#include <iostream>

#define FMT_HEADER_ONLY
#include "fmt/format.h"

using fmt::print;

int main()
{
    auto num {777};
    auto name {"James"};

    print("Number is {}\n", num);
    print("Name is {}\n", name);
}

 

0 głosów
odpowiedź 10 grudnia 2022 przez pasjonat_algorytmiki Pasjonat (19,540 p.)
Ostatnio na potrzeby Olimpiady Informatycznej testowałem cin i cout, scanf i printf i ->

1 Wczytywanie danych:

ios_base__sync_with_stdio(0); cin.tie(0) napisanie na początku programu i korzystanie z cina jest szybsze niż scanf.

2 Wypisywanie

Najwolniejsze są cout-y z pisaniem  << endl

Potem cout-y z pisaniem "\n"

Potem cout-y z pisaniem '\n'

Najszybsze są printfy. i tak korzystac z '\n;

Tak mi wyszło w moich testach.

Jak bardzo zalezy ci na szybkosci to możesz sobie własne wczytywanie pisać. Ale na ogół rożnicę są baaardzo małe.
komentarz 12 grudnia 2022 przez Oscar Nałogowiec (29,320 p.)

Printf testowałeś tak:

printf("Jakis komunikat\n");

czy tak:

printf("%s\n", "jakis komunikat");

?

komentarz 12 grudnia 2022 przez pasjonat_algorytmiki Pasjonat (19,540 p.)
Ogólnie miałem kilka zadań na OI, OIG, OIJ z tego co pamiętam to chyba koło 5, gdzie z cout-ami dostawałem 95pkt albo coś koło tego a z printfami 100. Były to zadania gdzie trzeba było bardzo dużo danych wypisywać. Oczywiście mówię o cout-ach z przyśpieszeniem -> ios_base__sync_with_stdio(0) oraz cout.tie(0). Bo bez tych linii to dramat.

Te 2. Na konkursach zazwyczaj wypisuje się zmienne.

Było to sprawdzane na szkopule. Nie znam się dokładnie, ale chyba nie zaburza to wiarygodności testów.

Podobne pytania

0 głosów
2 odpowiedzi 268 wizyt
0 głosów
1 odpowiedź 292 wizyt
pytanie zadane 11 października 2021 w C i C++ przez PatrykO2 Nowicjusz (240 p.)

92,579 zapytań

141,432 odpowiedzi

319,664 komentarzy

61,964 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...