Как ускорить команду grep / awk? - PullRequest
0 голосов
/ 28 мая 2019

Я собираюсь обработать текстовый файл (> 300 ГБ) и разделить его на небольшие текстовые файлы (~ 1 ГБ). Я хочу ускорить команды grep / awk.

Мне нужно выполнить поиск строки со значениями в столбце b, вот мои пути:

# method 1:
awk -F',' '$2 ~ /a/ { print }' input

# method 2:
grep -e ".a" < inpuy

Оба способа стоят 1 мин для каждого файла. Так как я могу ускорить эту операцию?


Пример входного файла:

a,b,c,d
1,4a337485,2,54
4,2a4645647,4,56
6,5a3489556,3,22
9,,3,12
10,0,34,45
24,4a83944,3,22
45,,435,34

Ожидаемый выходной файл:

a,b,c,d
1,4a337485,2,54
4,2a4645647,4,56
6,5a3489556,3,22
24,4a83944,3,22

Ответы [ 3 ]

3 голосов
/ 28 мая 2019

Как ускорить команду grep / awk?

Вы так уверены, что grep или awk является виновником вашей ощущаемой медлительности? Знаете ли вы о cut (1) или sed (1) ? Вы тестировали время для запуска wc (1) на ваших данных? Возможно, текстовый ввод-вывод отнимает много времени.

Пожалуйста, сравните несколько раз и используйте time (1) для сравнения вашей программы.

У меня есть высокопроизводительный рабочий стол Debian (с AMD 2970WX, 64 ГБ ОЗУ, системным диском на 1 ТБ SSD, многотерабайтными дисками данных SATA 7200 об / мин) и я просто запускаю wc для файла размером 25 ГБ (некоторые *.tar.xz архив) сидение на жестком диске занимает более 10 минут (измеряется с помощью time), а wc выполняет некоторую очень простую текстовую обработку, читая этот файл последовательно , поэтому должен работать быстрее чем grep (но, к моему удивлению, нет!) или awk на тех же данных:

wc /big/basile/backup.tar.xz  640.14s user 4.58s system 99% cpu 10:49.92 total

и (используя grep в том же файле для подсчета вхождений a)

grep -c a /big/basile/backup.tar.xz  38.30s user 7.60s system 33% cpu 2:17.06 total

общий ответ на ваш вопрос:

Просто Запись Умно (с эффективным O (log n) сложность времени структуры данных : красно-черные деревья или хеш-таблицы и т. Д.) эквивалентная программа на C или C ++ или Ocaml или большинстве других хороших языков и реализаций . Или купить больше оперативной памяти, чтобы увеличить кэш страницы . Или купите SSD для хранения ваших данных. И повторяйте свои тесты более одного раза (из-за кеша страниц).

предложение по вашей проблеме: используйте реляционную базу данных

Вероятно, использование простого текстового файла объемом 300 Гбайт не лучший подход. Наличие огромных текстовых файлов обычно неправильно и может быть неправильным, если вам нужно обработать несколько раз одних и тех же данных. Вам лучше предварительно обработать как-нибудь ..

Если вы повторите один и тот же grep поиск или awk выполнение для одного и того же файла данных более одного раза, рассмотрите вместо этого использование sqlite (см. также этот ответ) или даже некоторую другую реальную реляционную базу данных (например, с PostGreSQL или другую хорошую СУБД) для хранения и обработки исходных данных.

Таким образом, возможный подход (если у вас достаточно места на диске) может состоять в том, чтобы написать какую-то программу (на C, Python, Ocaml и т. Д.), Снабженную вашими исходными данными, и заполнить некоторую базу данных sqlite. Убедитесь, что у вас есть умные индексы базы данных и потребуется design достаточно хорошая схема базы данных , помня о нормализации базы данных .

2 голосов
/ 28 мая 2019

Используйте mawk, избегайте регулярных выражений и выполните:

$ mawk -F, '$2!=""' file
a,b,c,d
1,4a337485,2,54
4,2a4645647,4,56
6,5a3489556,3,22
10,0,34,45
24,4a83944,3,22

Дайте нам знать, сколько времени это заняло.

Я провел несколько тестов с 10M записями ваших данных, основываясь на результатах: используйте mawk и regex:

GNU awk и regex:

$ time gawk -F, '$2~/a/' file > /dev/null

real    0m7.494s
user    0m7.440s
sys     0m0.052s

GNU awk и без регулярных выражений:

$ time gawk -F, '$2!=""' file >/dev/null

real    0m9.330s
user    0m9.276s
sys     0m0.052s

mawk и без регулярных выражений:

$ time mawk -F, '$2!=""' file >/dev/null

real    0m4.961s
user    0m4.904s
sys     0m0.060s

mawk и регулярное выражение:

$ time mawk -F, '$2~/a/' file > /dev/null

real    0m3.672s
user    0m3.600s
sys     0m0.068s
0 голосов
/ 28 мая 2019

Я подозреваю, что ваша настоящая проблема в том, что вы вызываете awk несколько раз (возможно, в цикле), один раз для набора значений $ 2 и каждый раз генерируете выходной файл, например ::10000

awk -F, '$2==""' input > novals
awk -F, '$2!=""' input > yesvals
etc.

Не делайте этого, так как это очень неэффективно, поскольку он читает весь файл на каждой итерации. Сделайте это вместо:

awk '{out=($2=="" ? "novals" : "yesvals")} {print > out}' input

Это создаст все ваши выходные файлы одним вызовом awk. Как только вы получите около 15 выходных файлов, потребуется GNU awk для внутренней обработки дескрипторов открытых файлов или вам нужно добавить close(out) s при изменении $ 2 и использовать >> вместо >:

awk '$2!=prev{close(out); out=($2=="" ? "novals" : "yesvals"); prev=$2} {print >> out}' input

и это было бы более эффективно, если вы сначала отсортировали входной файл с помощью (требуется сортировка GNU для -s для стабильной сортировки, если вы хотите сохранить порядок ввода для уникальных значений $ 2):

sort -t, -k2,2 -s
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...