Почему туалет такой медленный?

17

Почему утилита wc такая медленная?

Когда я запускаю его для большого файла, это занимает примерно в 20 раз больше времени, чем md5sum:

MyDesktop:/tmp$ dd if=/dev/zero bs=1024k count=1024 of=/tmp/bigfile
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 0.687094 s, 1.6 GB/s

MyDesktop:/tmp$ time wc /tmp/bigfile 
         0          0 1073741824 /tmp/bigfile

real    0m45.969s
user    0m45.424s
sys     0m0.424s

MyDesktop:/tmp$ time md5sum /tmp/bigfile 
cd573cfaace07e7949bc0c46028904ff  /tmp/bigfile

real    0m2.520s
user    0m2.196s
sys     0m0.316s

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

(это на Ubuntu 13.04, 64 бит)

Джонни
источник
Примечание для тех, кто заботится только о количестве строк: wc -l <filename> намного быстрее для очень больших файлов.
EL

Ответы:

27

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

Действительно, если я изменю свою LANGпеременную локали по умолчанию en_US.UTF-8(UTF-8 является многобайтовым набором символов) и установлю ее на " C" (простой однобайтовый набор символов), wcона сможет использовать однобайтовую оптимизацию, что значительно ускоряет ее, займет всего около четверти, как и раньше.

Кроме того, он должен только проверять каждый символ, если он выполняет подсчет слов ( -w), длины строки ( -L) или символа ( -m). Если он выполняет только подсчет байтов и / или строк, он может пропустить обработку широких символов, а затем он работает очень быстро - быстрее, чем md5sum.

Я побежал через gprof, и функции, которые используются для обработки символов мультибайтные ( mymbsinit(), mymbrtowc(), myiswprint(), и т.д.) занимают около 30% только во время выполнения, а также код , который шаги через буфер является гораздо более сложным , поскольку он должен обрабатывать шаги переменного размера через буфер для символов переменного размера, а также вставлять любые частично завершенные символы, которые охватывают буфер, обратно в начало буфера, чтобы его можно было обработать в следующий раз.

Теперь, когда я знаю, что искать, я нашел несколько постов, в которых упоминается медлительность utf-8 с некоторыми утилитами:

/programming/13913014/grepping-a-huge-file-80gb-any-way-to-speed-it-up http://dtrace.org/blogs/brendan/2011/12/08 / 2000x эффективность выигрыша /

Джонни
источник
2
О, только что понял, что ты ОП. : p
Иван Чау
2
Хотя это самый популярный ответ, он не имеет значения. md5sumникогда не позволит вам посчитать номер слова и wcне вычислит хэш md5 файла! Это все равно, что спросить, почему моя машина такая медленная по сравнению с моей машинкой при написании текста.
user49468
5
@ user49468: Разумно предположить, что оба связаны с IO, так как оба должны читать каждый байт входного файла. Этот ответ доказывает, что wcпри обработке многобайтовых символов фактически привязан к процессору.
MSalters
2
@ user49468: wc и md5sum могут делать разные вещи, но оба читают файл и выполняют относительно простые вычисления, вычисляют контрольную сумму, подсчитывают байты, разделители слов и переводы строк. Ну, я думал , что это было просто, но не учел дополнительную сложность многобайтовых наборов символов. Это больше похоже на вопрос: «Почему моя машина едет в магазин в 20 раз быстрее, чем мой минивэн?» Вы ожидаете некоторую разницу между ними, но не разницу в 20 раз.
Джонни
1
В @Johnny you сравнения автомобилей и минивэнов отсутствует тот аспект, который предназначен для того, чтобы доставить вас в магазин. Таким образом, сравнение скорости на месте. Сравнение вашего автомобиля с полосой окраски автомобиля более подходит. Просто потому, что оба используют улицы, их скорости не имеют значения, так как маляр не подходит для покупок и наоборот.
user49468
1

Просто предположение, но вы сравниваете яблоки с апельсинами в отношении того, что wcделает с тем, что md5sumделает.

задание md5sum

Когда md5sumобрабатывает файл, он просто открывает файл как поток, а затем начинает запускать поток через функцию контрольной суммы MD5, которая требует очень мало памяти. По сути это связано с процессором и дисковым вводом / выводом.

Задача туалета

При wcзапуске он делает гораздо больше, чем просто анализирует файл за раз. Он должен фактически анализировать структуру файла, строки за раз, определяя, где находятся границы между символами и является ли это границей слова или нет.

пример

Подумайте о следующих строках и о том, как каждый из алгоритмов должен проходить через них при их разборе:

“Hello! Greg”
“Hello!Greg”
“Hello\nGreg”
“A.D.D.”
“Wow, how great!”
“wow     \n\n\n    great”
“it was a man-eating shark.”

Для MD5, он тривиально перемещается через эти строки символ за раз. Потому что wcон должен решить, что такое граница слова и линии, и отслеживать количество вхождений, которые он видит.

Дополнительные wc обсуждения

Я нашел эту проблему кодирования с 2006 года, которая обсуждает реализацию wcв .NET. Трудности довольно очевидны, когда вы смотрите на некоторые псевдокоды, так что это может помочь пролить свет на то, почему wcэто происходит намного медленнее, чем другие операции.

SLM
источник
1
Вы описываете что-то отличное от стандартной команды Unix wc (по крайней мере, не той, которая поставляется с Ubuntu). Этот туалет не считает уникальные слова, только слова, так что «привет, привет» - это 3 слова, а не 2.
Джонни
Исходя из этой теории, кажется, что более простая задача, такая как подсчет строк, будет выполняться быстрее. Изменяет ли 'wc' для указания количества строк существенные изменения результатов? 'wc -l'
Джошуа Миллер
@ Джонни - я никогда не говорил, что это уникальные слова, которые ты сказал. wcсчитает несколько вещей при разборе файла. Он считает количество слов, строк и байтов при анализе файла. Прочитайте справочную страницу!
SLM
@JoshuaMiller - Непонятно, wcограничивает ли указание только подсчетом строк свой внутренний анализ, чтобы он учитывал только эти вещи или только сообщал о результатах строк, даже если он все еще подсчитывал.
SLM
@slm Вы сказали, что он считает уникальные слова, ваш пример говорит: «Привет! Грег »приводит к Привет 1, Грег 1 , то есть подсчитывает для каждого слова. И проект .Net, на который вы ссылались, говорит: «Одна из его основных задач - пройти через набор данных и подсчитать количество повторений данного слова. Например, с учетом предложения« Привет, да, привет », он скажет вам, что слово Hello использовалось дважды, а слово yes использовалось один раз. " Хотя на самом деле результат эха "Привет, да привет" | wc --words , «3», а не «Hello: 2, Yes: 1»
Джонни