%20%D0%BC%D0%B5%D0%B4%D0%BB%D0%B5%D0%BD%D0%BD%D0%B5%D0%B5%2C%20%D1%87%D0%B5%D0%BC%20getc()%3F%20.png)
Почему read
медленнее, чем getc
?
Например, это:
for (;;) {
chr++;
amr=read(file1, &wc1, 1);
amr2=read(file2, &wc2, 1);
if (wc1 == wc2) {
if (wc1 == '\n')
line++;
if (amr == 0) {
if (eflg)
return (1);
return (0);
}
continue;
}
медленнее, чем это:
for (;;) {
chr++;
c1 = getc(file1);
c2 = getc(file2);
if (c1 == c2) {
if (c1 == '\n')
line++;
if (c1 == EOF) {
if (eflg)
return (1);
return (0);
}
continue;
}
getc
использует read
системный вызов, так почему же он медленнее?
решение1
Поскольку getc() буферизует считанные данные перед их возвратом, поэтому вызов getc()
не обязательно приводит к вызову read()
. read()
— это системный вызов, который занимает гораздо больше времени, чем обычный вызов функции, поскольку у ядра больше операций для выполнения. Когда вы входите в пространство ядра, он изменяет ваш стек, сохраняет весь контекст, обрабатывает прерывания для маскировки, а на другом конце, когда он завершается, он восстанавливает контекст, прерывания, возвращает ваш стек пользовательского пространства обратно... Вот почему getc() предпочтительнее, потому что он экономит вам важные накладные расходы, если у вас уже есть доступные буферизованные данные.
решение2
Все сводится к тому, что чтение с дисков ориентировано на блоки: чтобы прочитать один байт с диска, оборудование в конечном итоге считывает блок (512 или 1024 или какое-то число) байтов, буферизует все это, передает ядру. Если вы читаете байт 0 файла из блока 0 файла, выполняете некоторую работу, затем читаете байт 1 из файла, ядро, вероятно, снова считывает блок 0 файла. И снова для байта 2, и снова для байта 3. Да, есть потенциальное кэширование, как в ядре, так и в самом диске, но ядро обрабатывает много процессов, так что, возможно, и нет.
Каждый read()
вызов также должен изменить состояние ЦП из состояния пользователя в состояние ядра. Как минимум, меняются отображения памяти. Вероятно, происходит много других не столь очевидных вещей. Это тоже может занять время.
Системный read()
вызов изменяет состояние ЦП и может повлечь за собой дисковый ввод-вывод. getc()
может буферизировать целый блок диска (или больше) в пользовательском пространстве, поэтому, возможно, 512 вызовов заставят getc()
ядро прочитать один блок диска с одним изменением состояния. Если вы посмотрите, stdio.h
то найдете макрос для константы BUFSIZ
, которая должна быть эффективным (диск-блок-множество) размером для read()
или write()
системный вызов файла на диске.