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

Почему буферизация на С++ важна?

Я пытался напечатать "Hello World" 200 000 раз, и он взял меня навсегда, поэтому я должен остановиться. Но сразу после добавления массива char для работы в качестве буфера понадобилось менее 10 секунд. Почему?

Перед добавлением буфера:

#include <iostream> 
using namespace std;

int main() {
        int count = 0;
        std::ios_base::sync_with_stdio(false);
        for(int i = 1; i < 200000; i++)
        {       
                cout << "Hello world!\n";
                count++;
        }
                cout<<"Count:%d\n"<<count;
return 0;
}

И это после добавления буфера:

#include <iostream> 
using namespace std;

int main() {
        int count = 0;
        std::ios_base::sync_with_stdio(false);
        char buffer[1024];
        cout.rdbuf()->pubsetbuf(buffer, 1024);
        for(int i = 1; i < 200000; i++)
        {       
                cout << "Hello world!\n";
                count++;
        }
                cout<<"Count:%d\n"<<count;
return 0;
}

Это заставляет меня думать о Java. Какие преимущества использования BufferReader для чтения в файле?

4b9b3361

Ответ 1

Для подставки файловых операций запись в память (ОЗУ) всегда выполняется быстрее, чем запись в файл на диске напрямую.

Для иллюстрации давайте определим:

  • каждая запись операции ввода-вывода в файл на диске стоит 1 мс
  • каждая запись операции ввода-вывода в файл на диске по сети стоит 5 мс
  • каждая запись операции ввода-вывода в память составляет 0,5 мс

Скажем, нам нужно записать несколько файлов в файл 100 раз.

Случай 1: Прямая запись в файл на диске

100 times x 1 ms = 100 ms

Случай 2: Прямая запись в файл на диске через сеть

100 times x 5 ms = 500 ms

Случай 3: Буферизация в памяти перед записью в файл на диске

(100 times x 0.5 ms) + 1 ms = 51 ms

Случай 4: Буферизация в памяти перед записью в файл на диске через сеть

(100 times x 0.5 ms) + 5 ms = 55 ms

Заключение

Буферизация в памяти всегда быстрее, чем прямое управление. Однако, если ваша система неактивна в памяти и должна меняться с файлом страницы, она будет медленной снова. Таким образом, вы должны балансировать операции ввода-вывода между памятью и диском/сетью.

Ответ 2

Основная проблема с записью на диск заключается в том, что время, затраченное на запись, не является линейной функцией числа байтов, а аффинной с огромной константой.

В вычислительных терминах это означает, что для IO у вас хорошая пропускная способность (меньше памяти, но довольно неплохая), однако у вас низкая латентность (чуть лучше, чем в обычной сети).

Если вы посмотрите на статьи оценки жесткого диска или SSD, вы заметите, что тесты чтения/записи разделены на две категории:

  • пропускная способность при случайных чтениях
  • пропускная способность при непрерывных чтениях

Последний обычно значительно больше первого.

Обычно ОС и библиотека IO должны абстрагировать это для вас, но, как вы заметили, если ваша подпрограмма интенсивна, вы можете увеличить ее размер. Это нормально, библиотека, как правило, предназначена для всех видов использования и, таким образом, обеспечивает хорошую среднюю среду для средних приложений. Если ваше приложение не является "средним", оно может работать не так быстро, как могло бы.

Ответ 3

Какой компилятор/платформа вы используете? Я не вижу здесь существенной разницы (RedHat, gcc 4.1.2); обе программы занимают 5-6 секунд (но "пользовательское" время составляет около 150 мс). Если я перенаправляю вывод в файл (через оболочку), общее время составляет около 300 мс (поэтому большая часть из 6 секунд будет потрачена на то, чтобы моя консоль догнала программу).

Другими словами, вывод должен быть буферизирован по умолчанию, поэтому мне любопытно, почему вы видите такое огромное ускорение.

3 касательно связанных примечаний:

  • В вашей программе есть ошибка "один за другим" в том, что вы печатаете 199999 раз вместо указанных 200000 (либо начинайте с i = 0, либо заканчивайтесь на i <= 200000)
  • Вы смешиваете синтаксис printf с синтаксисом cout при выводе count... исправление для этого достаточно очевидно.
  • Отключение sync_with_stdio дает небольшое ускорение (около 5%) для меня при выводе на консоль, но при перенаправлении к файлу это влияние незначительно. Это микро-оптимизация, которую вам, вероятно, не понадобится в большинстве случаев (IMHO).

Ответ 4

Если у вас есть буфер, вы получаете меньше фактических вызовов ввода-вывода, что является медленной частью. Во-первых, буфер заполняется, затем один вызов ввода-вывода выполняется для сброса буфера. Будет одинаково полезен в Java или любой другой системе, где медленный ввод-вывод.

Ответ 5

Функция cout содержит много скрытой и сложной логики, идущей полностью вниз по ядру, поэтому вы можете писать текст на экран, когда вы используете буфер таким образом, что по существу выполняете пакетный запрос, вместо того чтобы повторять сложные вызовы ввода/вывода.