Нужно что-то быстрее, чем «wc -l»


12

Для действительно большого файла, такого как 1 ГБ, wc -lбывает медленно. У нас есть более быстрый способ подсчета количества новых строк для конкретного файла?


25
Купить диски быстрее? Учитывая, что каждый байт входных данных должен проверяться на предмет его 0x0Aотсутствия, ввод / вывод, несомненно, является узким местом.
thrig

2
Если вы подозреваете, wcчто у вас слишком много накладных расходов, вы можете попробовать реализовать свои собственные foreach byte in file: if byte == '\n': linecount++. Реализованный в C или ассемблере, я не думаю, что он станет быстрее, за исключением, возможно, пространства ядра в ОСРВ с наивысшим приоритетом (или даже для этого использует прерывание - вы просто не можете ничего сделать с системой). .. хорошо, я отвлекся ;-))
Мерфи

3
И просто для того, чтобы почувствовать масштаб, я быстро просмотрел time wc -l some_movie.aviнекэшированный файл, в результате чего 5172672 some_movie.avi -- real 0m57.768s -- user 0m0.255s -- sys 0m0.863s. Что в основном доказывает правильность @thrig, I / O разрушает вашу производительность в этом случае.
Мерфи

10
Лучший способ показать, что это узкое место дискового ввода-вывода, выполнить time wc -l some_large_file_smaller_than_cacheдважды подряд и посмотреть, как быстро выполняется вторая операция, а затем time wc -l some_large_file_larger_than_cacheпосмотреть, как не меняется время между запусками. Для файла размером ~ 280 МБ время идет от 1,7 до 0,2 секунд, но для файла объемом 2 ГБ - 14 секунд оба раза.
EightBitTony

1
Как медленно, слишком медленно для вас? Что /usr/bin/time wc -l <file>говорит? Какое у вас оборудование? Это быстрее, если вы запускаете команду несколько раз? Нам действительно нужно больше информации;)
marcelm

Ответы:


21

Вы можете попробовать написать на C:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(){
  char buf[BUFSIZ];
  int nread;
  size_t nfound=0;
  while((nread=read(0, buf, BUFSIZ))>0){
    char const* p;
    for(p=buf; p=memchr(p,'\n',nread-(p-buf)); nfound++,p++) {;}
  }
  if(nread<0) { perror("Error"); return 1; }
  printf("%lu\n", nfound);
  return 0;
}

Сохранить, например, wcl.cскомпилировать, например, с помощью gcc wcl.c -O2 -o wclи запустить с

<yourFile ./wcl

Это находит переводы строк в 1 Гб файла в моей системе примерно за 370 мс (повторные запуски ). (Увеличение размеров буфера немного увеличивает время, которое следует ожидать - BUFSIZ должен быть близок к оптимальному). Это очень сравнимо с ~ 380 мс, которые я получаю wc -l.

Mmaping дает мне лучшее время около 280 мс , но оно, конечно, имеет ограничение, ограниченное реальными файлами (без FIFOS, без ввода с терминала и т. Д.):

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
  struct stat sbuf;
  if(fstat(0, &sbuf)<0){ perror("Can't stat stdin"); return 1; }

  char* buf = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, 0/*stdin*/, 0/*offset*/);
  if(buf == MAP_FAILED){ perror("Mmap error"); return 1; } 

  size_t nread = sbuf.st_size, nfound=0;
  char const* p;
  for(p=buf; p=memchr(p,'\n',nread-(p-buf)); nfound++,p++) {;}

  printf("%lu\n", nfound);
  return 0;
}

Я создал свой тестовый файл с:

 $ dd if=/dev/zero of=file bs=1M count=1042 

и добавил несколько тестовых новых строк с:

 $ echo >> 1GB 

и шестнадцатеричный редактор.


Я был удивлен результатом mmap TBH. Раньше я думал, что mmaping быстрее, чем чтение / запись, но потом я увидел некоторые тесты Linux, которые показали обратное. Похоже, это очень верно в этом случае.
PSkocik

4
mmap собирается добиться гораздо лучших результатов в Linux, потому что в настоящее время он будет отображаться на огромных страницах, а промахи TLB - это sloooowwwwwww.
до

Может быть некоторое преимущество при чтении разных частей файла в отдельных потоках (например, с помощью forцикла OpenMP ), так что некоторый прогресс может быть достигнут, пока один поток останавливается в ожидании ввода. Но, с другой стороны, это может затруднить планирование ввода / вывода, поэтому все, что я могу порекомендовать, это попробовать и измерить!
Тоби Спейт

read()Версия может извлечь выгоду из упреждающего чтения.
Бармар

1
@TobySpeight Да, многопоточность может ускорить его. Кроме того, просмотр двух байтов за раз с помощью таблиц поиска 2 ^ 16 обеспечил довольно хорошую скорость в прошлый раз, когда я играл с ним.
PSkocik

18

Вы можете улучшить решение, предлагаемое @pskocik, уменьшив количество звонков до read. Существует много вызовов для чтения BUFSIZфрагментов из файла объемом 1 ГБ. Обычный подход к этому заключается в увеличении размера буфера:

  • просто ради интереса попробуйте увеличить размер буфера в 10 или 100 раз. На моем Debian 7 BUFSIZэто 8192. С оригинальной программой это 120 тысяч операций чтения. Вы, вероятно, можете позволить себе входной буфер 1 Мб, чтобы уменьшить его в 100 раз.
  • для более оптимального подхода приложения могут выделить буфер размером с файл, для чего требуется одна операция чтения. Это работает достаточно хорошо для «маленьких» файлов (хотя некоторые читатели имеют более 1 ГБ на своей машине).
  • наконец, вы можете поэкспериментировать с отображаемым в память вводом / выводом, который обрабатывает распределение как таковое.

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

То же самое относится и к mmap(в другом случае, все еще в моем списке дел для включения в FAQ, разработчик сообщил о очень хороших результатах в сценарии, где дисковый кеш был реальной причиной улучшения). Разработка эталонных тестов требует времени и усилий, чтобы проанализировать причины хорошей (или плохой) производительности.

Дальнейшее чтение:


2
Вы переоцениваете влияние размеров буфера выше определенного порога. Как правило, увеличение размера буфера выше 4 КБ мало помогает, и на самом деле может иметь пагубные последствия, поскольку может вытолкнуть буфер из кэша L1. На моей машине тестирование с ddиспользованием буферов 1 МБ медленнее, чем 8 КБ. Значение по умолчанию для wc 8 КБ выбрано довольно хорошо, оно будет близко к оптимальному для широкого диапазона систем.
marcelm
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.