Чому wc так повільно?


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 <ім'я файла> набагато швидше для дуже великих файлів.
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 файлу! Це як запитати, чому моя машина така повільна порівняно з моєю машинкою при написанні тексту.
користувач49468

5
@ user49468: Розумно припускати, що обидва пов'язані IO, оскільки обидва повинні читати кожен байт вхідного файлу. Ця відповідь доводить, що wcнасправді пов'язана з процесором при обробці багатобайтових символів.
MSalters

2
@ user49468: wc і md5sum можуть робити різні речі, але обидва читають файл і роблять порівняно прості обчислення, один обчислює контрольну суму, один рахує байти, розділювачі слів та нові рядки. Ну, я думав, що це просто, але не враховував зайву складність багатобайтових наборів символів. Це більше, як запитати "Чому моя машина в 20 разів швидше їде в магазин, ніж мій мінівен?" Ви очікували б різниці між ними, але не 20X різницею.
Джонні

1
@Johnny у вас порівняння автомобілів / мікроавтобусів не має такого аспекту, який обидва призначені для транспортування вас до магазину. Тому порівняння швидкості існує. Порівнювати свій автомобіль з транспортним засобом для розфарбовування смужок є більш підходящим. Тільки тому, що обидва користуються вулицями, їх швидкість не є актуальною, оскільки художник-смугастий не підходить для покупок і навпаки.
user49468

1

Лише здогадка, але ти порівняєш яблука з апельсинами щодо того, що wcробиш проти того, що md5sumробиш.

Завдання md5sum

При md5sumобробці файлу він просто відкриває файл як потік, а потім починає запускати потік через функцію контрольної суми MD5, якій потрібно дуже мало пам'яті. Це по суті пов'язаний процесор і диск вводу / виводу.

завдання wc

Під час 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нього , щоб вирішити , що це слово і лінії кордону і стежити за кількість входжень , що він бачить.

Додаткові обговорення в туалеті

Я знайшов цю проблему кодування з 2006 року, яка обговорює реалізацію wcв .NET. Труднощі досить очевидні, коли ви дивитесь на деякий псевдо-код, тому це може допомогти почати проливати світло на те, чому, wcздається, відбувається набагато повільніше, ніж інші операції.


1
Ви описуєте щось інше, ніж стандартна команда wc Unix (принаймні, не та, яка постачається з Ubuntu). Цей туалет не враховує унікальних слів, а лише слова, тому "привіт світ привіт" - це 3 слова, а не 2.
Джонні

Виходячи з цієї теорії, це звучить як більш просте завдання, як підрахунок рядків, піде швидше. Чи зміна 'wc' для визначення кількості рядків суттєво змінює результати? 'wc -l'
Джошуа Міллер

@Johnny - Я ніколи не казав, що це враховує унікальні слова, які ти це сказав. wcпідраховує кілька речей під час аналізу файлу. Він підраховує кількість слів, рядків та байтів під час розбору файлу. Прочитайте сторінку чоловіка!
slm

@JoshuaMiller - Незрозуміло, чи говорить wcлише обчислення рядків, це внутрішній синтаксичний аналіз, щоб він рахував лише ці речі, або лише лише повідомляє результати рядків, хоча він все-таки підраховував усе.
slm

@slm Ви сказали, що він нараховує унікальні слова, ваш приклад говорить: «Привіт! Greg "приводить до Hello 1, Greg 1 , тобто рахується для кожного слова. І проект .Net, з яким ви пов’язані, говорить: "Одне з його головних завдань - пройти набір даних і підрахувати кількість повторень певного слова. Наприклад, давши речення" Привіт, так привіт ", це скаже вам, що слово "Привіт" вживалося двічі, а слово "так" вживалося один раз ". Хоча насправді результат відлуння "Привіт, так привіт" | wc --words , це "3", а не "Привіт: 2, так: 1"
Джонні
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.