Размер буфера в C++

Я наблюдаю следующее поведение с методом библиотеки C++ Std std::ostream::write().

Для буферизации данных я использую следующий C++ API

std::ofstream::rdbuf()->pubsetbuf(char* s, streamsize n)

Это прекрасно работает (проверено с помощью утилиты strace), пока размер данных (datasize) мы записываем в файловый поток, используя

std::ofstream::write (const char* s, datasize n)

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

например Если я установлю размер буфера на 10 КБ и запишу около 512 байт за раз, strace покажет, что несколько операций записи были объединены в одну запись.

writev(3, [{"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 9728}, {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 512}], 2) = 10240 ( 10 KB )
writev(3, [{"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 9728}, {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 512}], 2) = 10240
...

но когда я пишу 1024 байта за раз (оставляя буфер фиксированным до 10 КБ), теперь strace показывает мне, что он не использует буфер, и каждый вызов ofstream::write преобразуется в системный вызов записи.

writev(3, [{NULL, 0}, {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 1024}], 2) = 1024 ( 1KB )
writev(3, [{NULL, 0}, {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 1024}], 2) = 1024
...

Есть ли какой-либо вызов C++ API или параметр настройки Linux, который мне не хватает?


person Abhishek    schedule 18.03.2014    source источник
comment
Почему тебе не все равно? Вы не можете использовать std::fflush манипулятор?   -  person Basile Starynkevitch    schedule 18.03.2014
comment
Нет такого понятия. Есть манипулятор std::flush, но он вызывает сброс, что противоречит тому, чего хочет OP.   -  person Sebastian Redl    schedule 18.03.2014
comment
@SebastianRedl Честно говоря, ОП даже не сказал, чего он хочет.   -  person Bartek Banachewicz    schedule 18.03.2014
comment
Запись 1024 байт за раз эффективна. В современной архитектуре очень мало пользы от увеличения записи.   -  person Klas Lindbäck    schedule 18.03.2014
comment
@KlasLindbäck Я не согласен: при пропускной способности шины памяти 10 ГБ / с копирование 1 КБ занимает примерно 100 нс. Системный вызов обычно занимает несколько микросекунд. Я бы сказал, что для того, чтобы накладные расходы на системные вызовы стали незначительными, требуется примерно 100 КБ.   -  person cmaster - reinstate monica    schedule 18.03.2014
comment
@cmaster +1 Кажется, ты прав. На моем сервере RHEL6 производительность записи в значительной степени выравнивается при записи 8 КБ (что равно размеру моей страницы на диске, но это может быть совпадением). Запись 1 КБ заняла почти в 3 раза больше времени, а запись 100 КБ заняла столько же времени, сколько запись 8 КБ. Причиной скачка производительности может быть ограничение физической записи на дисковую систему (100 Мб за 0,35 с).   -  person Klas Lindbäck    schedule 18.03.2014
comment
На самом деле я пытаюсь уменьшить количество системных вызовов записи, выполняемых моим приложением, размер буфера по умолчанию C++ составляет около 8 КБ (системный вызов записи после того, как он накопил 8 КБ данных, но это также работает, когда данные, которые я пишу, меньше чем 1к)   -  person Abhishek    schedule 18.03.2014


Ответы (2)


Это деталь реализации libstdc++, реализованная в строке 650 bits/fstream.tcc. По сути, если запись больше 2 ^ 10, буфер будет пропущен.

Если вам нужно обоснование этого решения, я предлагаю вам отправить письмо в список разработчиков libstdc++.

http://gcc.gnu.org/ml/libstdc++/

person Sebastian Redl    schedule 18.03.2014
comment
Отправил письмо в libstdc++ по почте, и да, это своего рода жестко запрограммированное поведение. Чтобы добавить далее, я выполнил тот же тестовый пример в ОС Sun (компилятор Sun Studio CC), поведение там другое, он учитывает заданный буфер (без границы на 1024) - person Abhishek; 30.03.2014
comment
Кажется, это все еще актуально сегодня (нашел жестко закодированную константу здесь github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/). - person iMineLink; 14.06.2021

Похоже, кто-то, пишущий реализацию stdlib, сделал «оптимизацию», не задумываясь об этом. Таким образом, единственным обходным путем для вас было бы избегать C++ API и использовать стандартную библиотеку C.

Это не единственная субоптимальность в реализации GNU/Linux стандартной библиотеки C++: на моей машине malloc() на 100 циклов быстрее, чем стандартная void* operator new (size_t size)...

person cmaster - reinstate monica    schedule 18.03.2014
comment
new() вызывает конструктор, а malloc() нет. И делает еще несколько приятных вещей. Сравнивать скорость обоих просто нонсенс. - person scai; 18.03.2014
comment
@scai Извините, что противоречу, но именно поэтому я дал всю подпись: я не говорю о ключевом слове new и даже не говорю об операторах new, специфичных для класса, я говорю о одна глобальная функция, которая сама производит выделение, и ничего больше. Это функция, которую вы можете легко реализовать, просто вызвав malloc(), которая является основой для моего измерения. - person cmaster - reinstate monica; 18.03.2014