Самый быстрый `uniq` инструмент в Linux

8

У меня большой текстовый файл (1,5 Г),

Я хочу знать, какой самый быстрый и надежный инструмент в Linux.

Я обычно использую:

awk '!x[$0]++' file.txt

Но когда я использую htopкоманду, я вижу, что использование моей памяти увеличивается.

Я хочу знать, что является самым быстрым и надежным для больших файлов.

uniq?
sort?
sed?
awk?

Почему?

MLSC
источник
Вы пытались запустить их, возможно, с time?
Чороба
время важно, а также использование памяти и надежность (я имею в виду, кто из них делает свою работу точно)
MLSC
Пока нет ... Но я делал некоторые тесты раньше ... и где-то спрашивал, некоторые ребята сказали мне, что awk - лучший .. но в htop ... Я вижу, что использование памяти увеличивается
MLSC
3
@MortezaLSC: это компромисс. Чем быстрее программа, тем больше памяти используется.
cuonglm

Ответы:

16

Давайте рассмотрим, как работает каждое решение.

  • uniqЭто требует, чтобы файл уже был отсортирован. Если нет, sortсначала нужно передать его по конвейеру , что означает, что sortнеобходимо прочитать весь файл в память, переупорядочить его ( O(n log n)), а затем записать в конвейер. Работа uniqочень дешевая, так как нужно только сравнить соседние строки ввода.

  • sort -uЭто сочетает в себе работу sort | uniq. Это должно собирать все уникальные входные данные в память, как это awkделает сценарий, но также тратить время на их сортировку перед созданием выходных данных. Это так O(n log n), хотя в данном случае nэто количество уникальных предметов, а не все входы. Так что лучше, чем труба.

  • sedЯ не уверен, почему вы перечислили это, так как я не могу придумать хороший способ сделать это sedвообще. Возможно, если вы сначала отсортируете его и передадите в sedскрипт, есть способ сравнить соседние строки. Так sedчто будет просто делать то uniq, что делает, и, uniqвероятно, делает это максимально эффективно.

  • awkЭто, вероятно, лучше, потому что он выполняет только минимальный объем работы, необходимый. Когда он читает каждую строку, он выполняет эффективный поиск хеша, чтобы увидеть, находится ли строка в ее памяти, и сохраняет только уникальные строки в качестве ключей хеша, а счетчик - в качестве значения. (Если строка ранее не присутствовала, условие будет истинным, поэтому строка будет напечатана. В противном случае это не будет.) Это использует O(n)время и O(uniq n)память.

Каждый метод будет использовать значительный объем памяти, либо для сортировки ввода, либо для отслеживания того, какие входы видели, чтобы они могли удалить дубликаты.

Barmar
источник
1
+1 Объяснение относительно awkтакже объясняет, почему это использует увеличивающиеся количества памяти. Все, что выполняет сортировку, в конечном итоге также будет делать это, только 1) оно, вероятно, будет использовать все сразу, 2) оно может использовать немного больше, в зависимости от количества уникальных и дублированных ключей.
Златовласка
@ Barmar, простите, но когда у меня большой файл (16 ГБ) с объемом памяти 8 ГБ, что же будет с моей памятью?
MLSC
8
@goldilocks, sortприбегает к временным файлам (интеллектуальным способом), чтобы избежать заполнения памяти. Его использование памяти связано. Граница настраивается с помощью некоторых видов реализации. Это более эффективно, чем позволить системе произвольно перезаписывать память на диск (что также влияет на приложения в системе).
Стефан Шазелас
Это правда. Так что, если вы столкнетесь с ситуацией, когда не awkхватает памяти, sortможет быть единственным решением, потому что оно было разработано для решения этой проблемы. С другой стороны, все это чтение и запись на диске замедлит его, поэтому, вероятно, это займет много времени. Если вы имеете дело с такими большими объемами данных, вам, вероятно, следует использовать СУБД, а не текстовые файлы.
Бармар
@ Barmar Как вы поняли, что время переупорядочения увеличивается как O(n log n)? Или просто ты знаешь это откуда-то еще?
Джимми
0

Я просто хотел указать, что GNU uniqкажется ужасно медленным, даже в отсортированном списке.

Я просто попытался получить список префиксов каталогов из списка отсортированных имен файлов:

$ pv all_files | cut -d '/' -f 1,2,3,4 | uniq > all_prefixes

36.7GiB 0:07:41 [81.4MiB/s]

$ pv all_files | cut -d '/' -f 1,2,3,4 | sort -u > all_prefixes2

36.7GiB 0:03:14 [ 193MiB/s]

$ pv all_files  | cut -d '/' -f 1,2,3,4 | awk '!x[$0]++' > all_prefixes3                                        
36.7GiB 0:02:18 [ 270MiB/s] 

sort -u кажется в два раза быстрее, чем uniq, и это при чтении сортировки из stdin и записи в stdout, поэтому я пока не вижу, чтобы это делало распараллеливание. Я понятия не имею, почему uniq должен быть намного медленнее, чем сортировать, поскольку он не должен сортировать список ...

Результат этой команды очень мал (есть много дубликатов), только 264 КБ и сортировка завершается сразу после выполнения pv.

Те же скорости остаются, если вы меняете порядок команд, мой поток ограничен здесь временем процессора, а не доступом к диску и кешами (у меня только 8 ГБ ОЗУ, и мой подкачка не используется)

Я запускаю это на машине fedora 31 с GNU Coreutils и UniQ и GNU AWK; локаль установлена ​​в en_US.UTF-8

ОБНОВЛЕНИЕ , так как это немного заинтриговало меня, я сделал еще несколько тестов, давайте разберем вырезанную часть и убедимся, что файл хорошо отсортирован

cat all_files | cut -d '/' -f 1,2,3,4 | sort -T . > test

Это займет 8,4 минуты. тест теперь 7,9 ГБ большой

давайте запустим эти инструменты в файле, а не в конвейере, это позволит этим инструментам выполнить дополнительную оптимизацию, например, sort будет multi thread. а также от более быстрого ssd.

Вы можете не заметить, что сортировка также занимает много памяти, так как она делает хитрые трюки с временными файлами в / tmp, которые могут быть tmpfs и будут у вас в памяти (попробуйте отсортировать файл больше, чем / tmp, вы попадете в пространство вопросы, поэтому мне нужен флаг -T. в приведенной выше команде)

$ time sort -u test > /dev/null
339.24user 3.54system 1:28.87elapsed 385%CPU (0avgtext+0avgdata 2365856maxresident)k
9555544inputs+0outputs (0major+591298minor)pagefaults 0swaps

$ time awk '!x[$0]++' test > /dev/null                                                                                                                             
51.15user 1.55system 0:52.94elapsed 99%CPU (0avgtext+0avgdata 10976maxresident)k
0inputs+0outputs (0major+1923minor)pagefaults 0swaps

$ time uniq test > /dev/null                                                                                                                                  
421.89user 2.76system 7:06.63elapsed 99%CPU (0avgtext+0avgdata 1980maxresident)k
52712inputs+0outputs (0major+79minor)pagefaults 0swaps

Так что, похоже, ваше awk-решение является самым быстрым из этих 3 , и на самом деле использует меньше всего памяти

update2 и теперь с более простым языком

$ export LC_ALL=c
$ time sort -u test > /dev/null                                                                                                                                             1.2m ? Tue Apr 21 17:09:22 2020
119.18user 3.64system 0:38.24elapsed 321%CPU (0avgtext+0avgdata 2013472maxresident)k

$ time awk '!x[$0]++' test > /dev/null                                                                                                                                1161ms ? Tue Apr 21 17:07:31 2020
67.23user 2.50system 1:10.16elapsed 99%CPU (0avgtext+0avgdata 10480maxresident)k
7187520inputs+0outputs (0major+1912minor)pagefaults 0swaps

$ time uniq test > /dev/null                                                                                                                                               
22.05user 2.02system 0:24.24elapsed 99%CPU (0avgtext+0avgdata 1488maxresident)k
2959648inputs+0outputs (1major+72minor)pagefaults 0swaps

На этот раз uniq действительно выигрывает гонку ... как намекает Стефан Шазелас в комментариях, установка вашего языка на C делает сортировку и uniq целой кучей быстрее!

Дженс Тиммерман
источник
Какая реализация sortа uniq? Какой язык?
Стефан Шазелас