2009-04-09 3 views
11

Я пишу программу, где исполнение очень важно, но не критично. В настоящее время я читаю текст из строки FILE*, и я использую fgets для получения каждой строки. После использования некоторых инструментов производительности я обнаружил, что в 20-30% случаев, когда приложение работает, оно находится внутри fgets.Прочитайте строку ввода быстрее, чем fgets?

Существуют ли более быстрые способы получения строки текста? Мое приложение однопоточное, без намерений использовать несколько потоков. Вход может быть из stdin или из файла. Заранее спасибо.

+0

Какова средняя длина (и возможная stdev) строк, которые ваша программа анализирует? Это помогает определить самый быстрый способ доступа к ним. – Juliano

+0

@ Юлиано, линии всегда имеют длину менее 260 символов. Я уже избегал цикла построения линии. – dreamlax

+0

Вы управляете форматом ввода? Не могли бы вы сделать его более компактным? – Dave

ответ

7

Вы не говорите, на какой платформе вы находитесь, но если она похожа на UNIX, тогда вы можете попробовать системный вызов read(), который не выполняет дополнительный уровень буферизации, который fgets() et все делаю. Это может немного ускорить работу, с другой стороны, это может замедлить работу - единственный способ узнать это - сосать его и посмотреть.

+0

Это оказалось самым быстрым методом для всех. В конце концов я спустился по этому маршруту. Это было проще, чем я думал, чтобы сделать «мою собственную буферизацию», и оказалось, что это намного быстрее (почти в 4 раза), чем использование 'fgets()'. – dreamlax

+0

По иронии судьбы, для меня преад в 4 раза хуже, чем у фэг. – abirvalg

2

Если данные поступают с диска, вы можете быть привязаны к IO.

Если это так, получите более быстрый диск (но сначала проверьте, что вы получаете максимальную отдачу от своего существующего ... некоторые дистрибутивы Linux не оптимизируют доступ к диску из коробки (hdparm)) , заранее поставите данные в память (например, скопировав их на RAM-диск) или будьте готовы подождать.


Если вы не связаны с IO, вы можете потратить много времени на копирование. Вы могли бы воспользоваться так называемыми методами нулевой копии. Что-то вроде памяти отображает файл и получает доступ только через указатели.

Это немного отличается от моего опыта, поэтому вам нужно немного почитать или подождать более осведомленной помощи.

BTW-- Возможно, вы попадаете в большую работу, чем проблема стоит; может быть быстрее машина будет решить все ваши проблемы ...

NB-- Это не ясно, что вы можете памяти на карту стандартного ввода либо ...

+0

Иногда это происходит с диска, иногда оно подается через stdin, но в обоих случаях время, потраченное на fgets, примерно одинаковое. Даже создание RAM-диска для файла не сильно ускоряет работу. – dreamlax

+0

После редактирования: проблема в том, что это приложение будет запущено на компьютере конечного пользователя, поэтому производительность очень важна. – dreamlax

3

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

Сорта, как и кэш ЦП, минимизирует время, в течение которого ЦП фактически возвращается в ОЗУ, вы можете использовать ОЗУ, чтобы свести к минимуму количество раз, когда вы фактически переходите на диск.

+0

Stdio уже буферизуется, не так ли? –

+0

Я так думаю, но я уверен, что это меньше, чем мегабайт, поэтому чтение больше, чем это должно помочь. – GManNickG

2

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

Это синтаксис -

setvbuf (InputFile, NULL, _IOFBF, BUFFER_SIZE); 

Где InputFile является FILE * в файл только открыто с помощью Еорепа() и BUFFER_SIZE является размером буфера (который выделяется этим вызовом для вас).

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

4
  1. Использование fgets_unlocked(), но внимательно прочитать то, что он делает первый

  2. Получить данные с fgetc() или fgetc_unlocked() вместо fgets().С помощью fgets() ваши данные дважды копируются в память, сначала библиотекой времени выполнения C из файла во внутренний буфер (поток ввода-вывода ввода-вывода буферизуется), затем из этого внутреннего буфера в массив в вашей программе

+0

Спасибо за предложение, но я забыл упомянуть, что использую Mac OS X. fgets_unlocked недоступен, так как это расширение GNU. Я рассмотрю использование fgetc_unlocked. – dreamlax

+0

Ну, OS X работает GCC, вы должны получить расширения GNU, правильно? –

+1

@Martin: Это не расширение компилятора GNU, а библиотека времени исполнения GNU C. – dreamlax

4

Прочтите весь файл за один проход в буфер.

Обработать линии из этого буфера.

Это самое быстрое решение.

0

Если ОС поддерживает его, вы можете попробовать асинхронное чтение файла, то есть файл считывается в память, в то время как процессор занят чем-то другим. Таким образом, код идет что-то вроде: ​ ​ ​ ​ ​

start asynchronous read 
loop: 
    wait for asynchronous read to complete 
    if end of file goto exit 
    start asynchronous read 
    do stuff with data read from file 
    goto loop 
exit: 

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

0

Посмотрите в fread(). Он читается намного быстрее для меня, особенно если для буфера для fread установлено значение 65536. Минусы: вам нужно много работать и, по сути, написать свою собственную функцию getline для преобразования из двоичного чтения в текст. Ознакомиться: file I/O

Смежные вопросы