Строки вида крупного файла количеством слов на строке (идеально параллельно)

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

17118 17136 17392
17064 17093 17376
17118 17136 17356 17318 12345
17118 17136 17356 17283
17007 17059 17116

Каждая из этих строк представляет уникальную клику (набор идентификаторов узла), и я хочу отсортировать эти строки в порядке убывания по количеству идентификаторов на строку. В случае примера выше, вот то, на что должен быть похожим вывод:

17118 17136 17356 17318 12345
17118 17136 17356 17283
17118 17136 17392
17064 17093 17376
17007 17059 17116

(Связи---т.е. строки с тем же количеством идентификаторов---могут быть отсортированы произвольно.)

Что является самым эффективным способом отсортировать эти строки.

Помните о следующих моментах:

  1. Файл, который я хочу отсортировать, мог быть больше, чем физическая память машины
  2. Большинство машин, что я работаю на этом, имеет несколько процессоров, таким образом, параллельное решение было бы идеально
  3. Идеальное решение просто было бы сценарием оболочки (вероятно, использующий вид), но я открыт для простых решений в Python или жемчуге (или любой язык, пока это делает задачу простой),
  4. Эта задача находится в некотором смысле очень легкий---, я только ищу любое старое решение, а скорее для простого и прежде всего эффективного решения

ОБНОВЛЕНИЕ 2: Лучшее решение

На основе сравнительного тестирования решений сделал предложение (см. ниже), вот лучшее решение (взятый от Vlad, который в свою очередь адаптировал его из других решений, предложенных здесь). Это довольно умно и даже не использует вид

for FILE in infile.* ; do
  awk '{ print >sprintf("tmpfile.%05d.%s", NF, FILE) }' \
    FILE=`basename $FILE` $FILE&
done
wait
ls -1r tmpfile.* | xargs cat >outfile
rm -f tmpfile.*

ОБНОВЛЕНИЕ 1: Сравнительное тестирование результатов предлагаемых решений

Для сравнительного тестирования я взял Клики, найденные в сети Oklahoma State Facebook. Неотсортированные файлы, которые содержат эти клики взгляд точно так же, как первый пример, который я показываю выше, содержу 46 362 546 строк, который приносит размеру файла до 6,4 ГБ. Клики почти равномерно распространены по 8 файлам. Система, на которой я тестирую это, содержит 4 физических процессора, каждого с 6 ядрами и 12 МБ кэш L2, для в общей сложности 24 ядер. Это также содержит 128 ГБ физической памяти. Поскольку строки, которые будут отсортированы, были разделены на 8 файлов, большинство этих решений использовало 8 (или 16) параллельные процессы.

Игнорируя первый наивный подход, я сравнил последних 5 предложений Vlad Romascanu (решение, которое я выбрал).

Первое решение не было слишком эффективно:

real    6m35.973s
user    26m49.810s
sys     2m14.080s

Я пытался использовать решения 2, 3, и 4, которые используют файлы FIFO, но каждый из них только использовал один процесс вида и таким образом требовался много времени (и таким образом, я уничтожил их, прежде чем они могли закончить) /

Последнее решение было самым быстрым:

real    1m3.272s
user    1m21.540s
sys     1m22.550s

Обратите внимание, что пользовательское время этого решения является 1m21 s, намного лучше, чем первые решения 26 минут.

5
задан Bobby 29 June 2011 в 12:12
поделиться

6 ответов

Наивный подход может быть простым:

awk '{ print NF " " $0 }' infile| sort -k1,1nr |
 awk '{ $1=""; print $0 }' >outfile

Это будет загружать до 3 ЦП. sort не ограничивается объемом доступной физической памяти, используйте переключатели -S и -T , чтобы настроить, сколько памяти использовать ( - S ), прежде чем обращаться к временным файлам во временном каталоге ( -T ) на достаточно большом (и в идеале быстром) разделе.

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

for FILE in infile.* ; do
  awk '{ print NF " " $0 }' $FILE | sort -k1,1nr >$FILE.tmp&
done
wait
sort -k1,1nr -m infile.*.tmp | awk '{ $1=""; print $0 }' >outfile
rm -f infile.*.tmp

Это будет использовать до N * 2 процессоров ; более того, последняя сортировка (сортировка слиянием) очень эффективна.

Дальнейшее уточнение для улучшения параллелизма до N * 2 + 1 за счет использования FIFO вместо промежуточных файлов, снова предполагая, что несколько входных файлов возможно:

for FILE in infile.* ; do
  mkfifo $FILE.fifo
  awk '{ print NF " " $0 }' $FILE | sort -k1,1nr >$FILE.fifo&
done
sort -k1,1nr -m infile.*.fifo | awk '{ $1=""; print $0 }' >outfile
rm -f infile.*.fifo

Если несколько входных файлов невозможно , вы можете смоделировать их (добавив накладные расходы ввода-вывода, которые, мы надеемся, будут амортизированы числом доступных процессов):

PARALLELISM=5 # I want 5 parallel instances
for N in `seq $PARALLELISM` ; do
  mkfifo infile.$N.fifo
  awk 'NR % '$PARALLELISM'=='$N' { print NF " " $0 }' infile |
    sort -k1,1nr >infile.$N.fifo&
done
sort -k1,1nr -m infile.*.fifo | awk '{ $1=""; print $0 }' >outfile
rm -f infile.*.fifo

Поскольку мы используем номер-строки по модулю, у нас хорошая локальность и файловая система В идеале кеш должен приближать затраты на чтение входного файла снова и снова в процессах $ PARALLELISM к нулю.

Еще лучше , чтение входного файла только один раз и циклическое перебор входных строк в несколько sort конвейеров:

PARALLELISM=5 # I want 5 parallel instances
for N in `seq $PARALLELISM` ; do
  mkfifo infile.$N.fifo1
  mkfifo infile.$N.fifo2
  sort -k1,1nr infile.$N.fifo1 >infile.$N.fifo2&
done
awk '{ print NF " " $0 >("infile." NR % '$PARALLELISM' ".fifo1") }' infile&
sort -k1,1nr -m infile.*.fifo2 | awk '{ $1=""; print $0 }' >outfile
rm -f infile.$N.fifo[12]

Вы должны измерить производительность для различных значений $ PARALLELISM затем выберите оптимальный.

РЕДАКТИРОВАТЬ

Как показано в других сообщениях, вы, конечно, можете использовать cut вместо окончательного awk (т.е. который удаляет первый столбец) для потенциально большей эффективности. :)

EDIT2

Обновлены все сценарии для указанного вами соглашения об именах файлов и исправлена ​​ошибка в последней версии.

Кроме того, используя новое соглашение об именах файлов, если ввод-вывод не является узким местом, тогда следует внести очень небольшую вариацию в решения dave / niry . возможно, будет еще эффективнее:

   for FILE in infile.* ; do
     awk '{ print >sprintf("tmpfile.%05d.%s", NF, FILE) }' \
       FILE=`basename $FILE` $FILE&
   done
   wait
   ls -1r tmpfile.* | xargs cat >outfile
   rm -f tmpfile.*
11
ответ дан 18 December 2019 в 09:06
поделиться
awk '{print length,$0}' test.txt | sort -nr | cut -d" " -f2-

Не уверен, насколько хорошо это будет работать, хотя сортировка может работать с ограничениями памяти, AFAIK.

0
ответ дан 18 December 2019 в 09:06
поделиться

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

10 split the file into N subfiles, one for each core/cpu
20 sort each partial file using the solutions suggested in some of the answers here
30 once every file is split and sorted, get the first line from each file and put it into a temporary file
40 get the second line from each file, put them in a second temporary file
50 repeat until you have number of temp files == number of cores
60 GOTO 20

В зависимости от количества проходов вам следует подойти отлично отсортированный файл.

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

ps: если частичные файлы все еще больше, чем доступная память, разделите их снова, пока они не поместятся (в зависимости от алгоритма сортировки, который вы используете для каждого файла). Но в этом случае вам нужно удвоить количество проходов, чтобы получить разумное приближение

ps2: Я также предполагаю, что вас интересует не идеально отсортированный файл, а больше статистическая значимость данных (например, как long - в среднем длинные строки и т. Д.).

0
ответ дан 18 December 2019 в 09:06
поделиться

Интересно, насколько быстро это будет работать:

#!/bin/sh
rm -rf /tmp/fb
mkdir /tmp/fb
cd /tmp/fb
awk '{ print $0 > NF }'
ls | sort -nr | xargs cat

Хотя это не использует преимущества большого количества ядер.

5
ответ дан 18 December 2019 в 09:06
поделиться

Так как вам действительно не нужно сортировать, просто скопируйте в сегменты, вы можете разделить файлы по количеству токенов, это будет самым быстрым:

perl -ne 'split/\s+/;$t=$#_+1;open $f[$t], sprintf(">%09d",$t) if $f[$t] eq "";$f=$f[$t];print $f $_;'

cat `ls -1r 0*`

кстати, диск будет узким местом, # ядер и использование не имеет значения.

1
ответ дан 18 December 2019 в 09:06
поделиться

Для создания чего-то эффективного я бы сделал что-то вроде следующего, разбор файла в два прохода:

В первом проходе читаем строку за строкой, записывая три вещи: номер строки, смещение файла и количество слов. Это можно паралеллировать без особых проблем (для заданий, которые начинаются со "случайных" строк в файле, просто добавьте соответствующий начальный номер после слов).

Теперь отсортируйте список из трех записанных вещей по количеству слов в строке. Затем пройдитесь по списку итерациями, стремясь к соответствующему стартовому смещению.

С точки зрения производительности, все поиски могут быть медленными, но это должно быть относительно небольшое потребление памяти, требуя только 3 ints для каждой строки.

0
ответ дан 18 December 2019 в 09:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: