Подтвердить что ты не робот

Endl и очистка буфера

В книге С++ primer в главе (1) упоминается следующее:

endl - специальное значение, называемое манипулятором, которое при записи на выходной поток имеет эффект написания новой строки для вывода и использовать буфер, связанный с этим устройством. Используя буфер, мы гарантируем, что пользователь увидит вывод, записанный в поток немедленно.

Что означает "промывка буфера" здесь?

4b9b3361

Ответ 1

Вывод обычно буферизуется до того, как он будет записан на предполагаемое устройство. Таким образом, при записи для медленного доступа к устройствам (например, файлы) ему не нужно обращаться к устройству после каждого отдельного символа.

Промывка означает опорожнение буфера и фактическое его запись на устройство.

Ответ 2

С++ iostreams буферизуются, это означает, что когда вы выходите в ostream, контент сразу не переходит к тому, что находится за потоком, например. stdout в случае cout. Реализация потока определяет, когда действительно отправлять буферизованную часть потока. Это делается по соображениям эффективности, было бы очень неэффективно записывать в байты байта сети или диска, путем буферизации этой проблемы.

Это означает, однако, что когда вы пишете сообщения об отладке в файл журнала и сбои вашей программы, вы можете потерять часть данных, которые вы написали в файле журнала через поток, поскольку часть журнала все еще может находиться в буфера потока и еще не записаны в фактический файл. Чтобы этого не произошло, вам необходимо сделать поток для очистки своих буферов либо явным вызовом метода flush, либо с помощью удобства endl.

Если, однако, вы просто записываете в файл регулярно, вы должны использовать \n вместо endl, чтобы предотвратить излишнюю очистку потока каждой строки, уменьшая производительность.

Отредактировано для включения этой заметки:

cin и cout имеют особые отношения, когда чтение из cin будет автоматически очищать cout заранее. Это гарантирует, что, например, подсказка, которую вы написали cout, будет фактически видна пользователю до того, как чтение из cin ждет ввода. Следовательно, даже в cout вы обычно не нуждаетесь в endl, но можете использовать \n вместо этого. Вы можете создавать такие отношения между другими потоками, связывая их вместе.

Ответ 3

Что означает "промывка буфера" здесь?

std::endl приводит к тому, что данные в потоковой внутренней промежуточной памяти (ее "буфере" ) "сбрасываются" (передаются) в операционную систему. Последующее поведение зависит от того, к какому типу устройства подключен поток, но в целом, промывка даст вид, что данные физически переданы связанному устройству. Однако внезапная потеря власти может победить иллюзию.

Эта промывка включает в себя overhead (потерянное время), и поэтому ее следует минимизировать, когда скорость выполнения является важной задачей. Минимизация общего влияния этих накладных расходов является основной целью буферизации данных, но эта цель может быть побеждена чрезмерной промывкой.


Фоновая информация

Вх/вых вычислительной системы, как правило, очень сложный и состоит из нескольких слоев абстракции. Каждый такой уровень может вводить определенное количество накладных расходов. Буферизация данных - это способ уменьшения этих накладных расходов за счет минимизации количества отдельных транзакций, выполняемых между двумя уровнями системы.

  • Буферизация на уровне системы/кэширования на уровне системы/кэширования. Для очень высокой активности даже система с произвольным доступом к памяти компьютера может стать узким местом. Чтобы решить эту проблему, ЦП виртуализирует обращения к памяти, предоставляя многоуровневые слои скрытых кешей (отдельные буферы которых называются линиями кэша). Эти кеши процессоров записывают ваши записи в память алгоритма (в соответствии с политика записи), чтобы минимизировать избыточные обращения на шине памяти.

  • Буферизация уровня приложения. Хотя это не всегда необходимо, для приложения нередко выделять куски памяти для аккумулирования выходных данных, прежде чем передавать их на ввод-вывод библиотека. Это дает основополагающее преимущество обеспечения произвольного доступа (если необходимо), но существенная причина для этого заключается в том, что он минимизирует накладные расходы, связанные с созданием вызовов библиотек, что может быть существенно более трудоемким, чем просто запись в массив памяти.

  • Буферизация библиотек ввода/вывода: библиотека потока я + С++, опционально управляет буфером для каждого открытый поток. Этот буфер используется, в частности, для ограничения количества системных вызовов для ядра операционной системы, поскольку такие вызовы имеют тенденцию иметь некоторые нетривиальные накладные расходы. Это буфер, который сбрасывается при использовании std::endl.

  • ядро ​​операционной системы и драйверы устройств. Операционная система направляет данные на конкретный драйвер устройства (или подсистему) на основе того, к какому устройству вывода подключен поток. На данный момент фактическое поведение может сильно варьироваться в зависимости от характера и характеристик этого типа устройства. Например, когда устройство является жестким диском, драйвер устройства может не инициировать немедленную передачу на устройство, а поддерживать собственный буфер, чтобы дополнительно минимизировать избыточные операции (так как диски также наиболее эффективно записываются в куски). Для явного сброса буферов уровня ядра может потребоваться вызвать функцию системного уровня, такую ​​как fsync() on Linux - даже закрытие связанного потока, не обязательно заставляет такой флеш.

    Примеры устройств вывода могут включать...

    • терминал на локальной машине
    • терминал на удаленной машине (через SSH или аналогичный)
    • данные отправляются в другое приложение через трубы или сокеты
    • множество вариантов устройств массового хранения и связанных файловых систем, которые могут быть (снова) локально подключены или распределены через сеть.
  • аппаратные буферы. Специфическое оборудование может содержать собственные буферы памяти. Например, жесткие диски содержат дисковый буфер, чтобы (среди прочего) разрешить физическую запись, не требуя системного CPU чтобы заниматься всем процессом.

Во многих случаях эти различные уровни буферизации, как правило, являются (в определенной степени) избыточными и, следовательно, по существу излишними. Однако буферизация на каждом уровне может обеспечить огромный прирост пропускной способности, если по каким-либо причинам другие слои не смогут обеспечить оптимальную буферизацию в отношении служебных данных, связанных с каждым уровнем.

Короче говоря, std::endl адресовал только буфер, который управляется библиотекой потоков IO С++ для этого конкретного потока. После вызова std::endl данные будут перенесены на управление уровнем ядра, а то, что происходит рядом с данными, зависит от множества факторов.


Как избежать накладных расходов std::endl


inline std::ostream & endl( std::ostream & os )
   {
   os.put( os.widen('\n') ); // http://en.cppreference.com/w/cpp/io/manip/endl
   if ( debug_mode ) os.flush(); // supply 'debug_mode' however you want
   return os;
   }

В этом примере вы предоставляете пользовательский endl, который можно вызвать с помощью или без вызова внутреннего вызова flush() (что и заставляет переводить в операционную систему). Включение флеша (с переменной debug_mode) полезно для сценариев отладки, где вы хотите проверить вывод (например, диск файл), когда программа завершилась, прежде чем чистое закрытие связанных потоков (что вынудило бы окончательный сброс буфера).

Ответ 4

При использовании std::cout операнд, используемый после оператора вывода (<<), сохраняется в буфере и не отображается на stdin (обычно в терминале или в командной строке), пока не встретится std::endl или std::cin, что вызывает сброс буфера, в том смысле, отображает/выводит содержимое буфера на stdin.

Рассмотрим эту программу:

#include <iostream>
#include <unistd.h>

int main(void)
{
    std::cout << "Hello, world";
    sleep(2);
    std::cout << std::endl;

    return 0;
}

Полученный результат будет:

через 2 секунды

Привет, мир

Ответ 5

Один простой код, чтобы показать вам эффекты буферизованного ввода-вывода в c++

Все вводимые вами данные буферизуются, а затем передаются программным переменным в случае входных данных.

Посмотрите на код ниже:

//program to test how buffered  I/O can have unintended effects on our program

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int a;
    char c;
    cin>>a;
    cin>>c;
    cout<<"the number is : "<<a;
    cout<<"\nthe character is : "<<c;
}

здесь мы объявили две переменные, одну int и одну char, если мы введем число как "12d34", это приведет к тому, что переменная int будет принимать только 12 в качестве значения, и она отбросит оставшуюся часть, которая все еще будет в буфере. И при следующем вводе переменная char автоматически примет значение "d", даже не спрашивая вас о вводе