#опытным
std::cout, std::cerr и std::cin - стандартные объекты потоков для работы с вводом-выводом в С++.
Но из С++ программы мы также можем вызывать и сишное апи для работы с IO. Например
scanf, printf. Они также могут читать из консоли и писать в нее.Но как связаны С++ и С апи для работы с IO? Мешают ли они друг другу и скремблят результаты операций? Или все хитрее?
Все хитрее)
И разбирать все будем на примере потока вывода std::cout, для других все аналогично, просто так нагляднее будет.
В базовой конфигурации std::cout и запись в stdout с помощью С апи синхронизированы. То есть следующие вызовы полностью эквивалентны(
c- символьная переменная):std::fputc(stdout, c);
// and
std::cout.rdbuf()->sputc(c);
Запись символа в буфер С++ объекта имеет тот же эффект, что и запись символа в буфер С потока.
На практике это означает, что синхронизированные потоки C++ не буферизуются, и каждая операция ввода-вывода на потоке C++ немедленно применяется к буферу соответствующего потока C. Это позволяет свободно смешивать C++ и C I/O операции.
Обращу внимание. Мы до сих пор говорим только об одном символе. И не спроста: для записи конкретного символа гарантируется синхронизация и потокобезопасность(thread-safety). Но записи в stdout из разных тредов могут перемешивать символы этих записей.
То есть при вызове:
std::cout << "Hello, World!" << std::endl;
по сути имплементация посимвольно записывает приветствие миру в stdout в цикле, вызывая std::putc(stdout, c). Обычно такие функции(типа putc) реализованы с помощью внутренних механизмов синхронизации, обеспечивая потокобезопасность.
И это предательски медленно! Поэтому дефолтные операции ввода-вывода в С++ такие медленные.
Синхронизируют запись каждого символа только трусы! Но мы-то с вами не трусы.
Можно отвязать С++ потоки от С потоков и сделать их независимыми. Тогда у С++ потоков появляется свой буфер, который работает оптимальнее, чем посимвольная запись. Это может дать сильный буст к производительности стандартных IO операций и по скорости они могут сравниться с сишными.
Чтобы отвязать потоки нужно самой первой строчкой main вызывать следующую функцию. Например так
int main()
{
std::ios::sync_with_stdio(false);
std::cout << "a\n";
std::printf("b\n");
std::cout << "c\n";
}
Вывод может изменяться в разных ситуациях, но вот здесь получился такой вывод:
a
c
b
Видно, что разное апи работает независимо и непоследовательно.
Если вам не нужна такая синхронизация, то выключайте ее полетите на третьей космической скорости бороздить просторы галактики.
Be fast. Stay cool.
#cppcore #optimization #goodoldc #compiler