Данные в табл. 13.1 относятся к целому ряду факторов: к времени выполнения системных вызовов read() и write(), времени переноса данных между буферами в пространстве памяти ядра и в пространстве пользовательской памяти, времени переноса данных между буферами ядра и диском. Давайте дополнительно рассмотрим последний фактор. Вполне очевидно, что перенос содержимого файла с вводимыми данными в буферную кэш-память неизбежен. Но мы уже видели, что возвращение из write() происходит сразу же после переноса данных из пользовательского пространства в буферную кэш-память ядра. Поскольку размер оперативной памяти в системе, используемой для тестирования (4 Гбайт), существенно превышает размер копируемого файла (100 Мбайт), можно предположить, что ко времени завершения работы программы файл с выводимыми данными фактически не будет записан на диск. Поэтому в качестве дальнейшего эксперимента мы запускаем программу, которая просто записывает произвольные данные в файл, используя различные размеры буферов write(). Результаты приведены в табл. 13.2.

Данные из табл. 13.2 также получены при использовании ядра версии 2.6.30 в файловой системе ext2 с размером блока 4096 байт. В каждой строке показаны усредненные значения после 20 запусков. Тестовая программа (filebuff/write_bytes.c) не приводится, но она доступна в исходном коде для этой книги.

Таблица 13.2. Время, требуемое для записи файла длиной 100 миллионов байт

Размер BUF_SIZE

Время (в секундах)

Затрачиваемое

Задействования центрального процессора

Общее

Пользователем

Системой

1

2

4

8

16

32

64

128

256

512

1024

72,13

36,19

20,01

9,35

4,70

2,39

1,24

0,67

0,38

0,24

0,17

72,11

36,17

19,99

9,32

4,68

2,39

1,24

0,67

0,38

0,24

0,17

5,00

2,47

1,26

0,62

0,31

0,16

0,07

0,04

0,02

0,01

0,01

67,11

33,70

18,73

8,70

4,37

2,23

1,16

0,63

0,36

0,23

0,16

4096

16 384

65 536

0,11

0,10

0,09

0,11

0,10

0,09

0,00

0,00

0,00

0,11

0,10

0,09

В табл. 13.2 показывается расход времени на совершение системных вызовов write() и на перенос данных из пространства пользователя в буферную кэш-память ядра с использованием различных размеров буферов для write(). Для бóльших размеров буфера заметна существенная разница с данными, показанными в табл. 13.1. Например, при размере буфера 65 536 байт затрачиваемое время в табл. 13.1 составляет 2,06 секунды, а в табл. 13.2 это же время равно 0,09 секунды. Дело в том, что в последнем случае дисковый ввод-вывод фактически не выполняется. Иными словами, основная часть времени в строках, соответствующих большим размерам буфера в табл. 13.1, затрачивается на считывание данных с диска.

Как будет показано в разделе 13.3, когда операции вывода намеренно блокируются до тех пор, пока данные не будут перенесены на диск, время, затрачиваемое на вызовы write(), существенно возрастает.

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

13.2. Буферизация в библиотеке stdio

Для того чтобы сократить количество системных вызовов, при работе с файлами на диске буферизация данных в большие блоки осуществляется внутри функций ввода-вывода библиотеки языка C (например, на fprintf(), fscanf(), fgets(), fputs(), fputc(), fgetc()). Таким образом, библиотека stdio берет на себя работу по буферизации данных для их вывода с помощью write() или ввода посредством read().

Задание режима буферизации stdio-потока

Функция setvbuf() позволяет выбрать способ буферизации, которую будет применять библиотека stdio.

#include

int setvbuf(FILE *stream, char *buf, int mode, size_t size);

Возвращает 0 при успешном завершении или ненулевое значение при ошибке

Перейти на страницу:

Похожие книги