Linux для всех

индекс
247,20

Распараллеливание задач в Linux

Потребовалось мне перекодировать некоторое количество видео-файлов. Для этого я написал следующий сценарий:

#!/bin/bash

recode() {
mencoder -o $2 $1 -ovc x264 -x264encopts bitrate=22000:keyint=50 -oac mp3lame -lameopts vbr=3:br=320 -fps 50
}
recode input/00108.mts 00108.avi
recode input/00109.mts 00109.avi
...
...


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


Первый способ: распараллеливание средствами mencoder



Можно задать опции mencoder. Для x264 кодека есть возможность указать количество потоков:
  • threads=<0-16>
    Использовать потоки для кодирования одновременно на нескольких процессорах (по умолчанию: 1). Это немного ухудшает качество кодирования. 0 или 'auto' — автоматически определить количество процессоров и использовать соответствующее количество потоков.

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

Второй способ: распараллеливание средствами Bash


Способ прост, запускаем процессы параллельно, например так:

(recode input/00108.mts 00108.avi
recode input/00109.mts 00109.avi
...
...) &
(recode input/00108.mts 00110.avi
recode input/00109.mts 00111.avi
...
...)

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

Третий способ: распараллеливание средствами GNU make


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

Makefile:
all: 00108.avi 00109.avi 00110.avi 00111.avi 00118.avi 00119.avi 00120.avi 00123.avi

VPATH = input:.
%.avi: %.mts
        mencoder -o $@ $< -ovc x264 -x264encopts bitrate=22000:keyint=50 -oac mp3lame -lameopts vbr=3:br=320 -fps 50

Получилось на удивление просто и коротко. В начале перечисляется список всех файлов, которые я хочу получить. За ним следует путь до исходных файлов и правило сборки. Запускать командой «make -j 2», чтобы работало одновременно 2 процесса.

(Первая статья на хабре, не судите строго)
+87
12 августа 2009, 17:07
76

комментарии (58)

0
librarian #
Оригинально, а я просто процессы переназначал на разные процессоры
+11
develop7 #
use xargs -P 2, luke!
+1
shulc #
Спасибо, не знал про «xargs -P».

наверно, вот так должно работать:
#!/bin/sh -e
opts='-ovc x264 -x264encopts bitrate=22000:keyint=50 -oac mp3lame -lameopts vbr=3:br=320 -fps 50'
( for n in 00108 00109 00110 00111 00118 00119 00120 00123; do
  echo $n
done ) | xargs -P 2 -J % mencoder -o input/%.mts %.avi $opts
0
vk2 #
или

xargs -P ... << STOP
00108
00109
00110
...
STOP


(вместо for)
+2
ivanrt #
Не знал об этой опции. С xargs получается так:
ls *.mts | sed 's/\(.*\).mts/\1.avi \1.mts/' | xargs -l1 -P2 echo mencoder -o
+2
gribozavr #
А ещё можно заменить ls на echo.
+2
Fragster #
А почему бы просто не сделать стек файлов, и как только какой-нить из форкнутых процессов завершает обработку своего файла — если есть — берет из стека следующий…
0
ivanrt #
Так я и собирался сделать, но пока размышлял над деталями реализации вспомнил о make.
+1
Mezomish #
А разве не в этом состоит решение, предложенное автором? Задаётся набор («стек») файлов, указывается, каким образом их обрабатывать и затем указывается, какое количество рабочих процессов использовать. Ровно то, что Вы сказали — но в 3 строки (+аргумент при запуске make).

У Вас есть вариант короче и изящнее? Поделитесь :)
0
develop7 #
–1
Mezomish #
Короче — да, изящнее — … хм… %)
0
Fragster #
ну, не изящнее, но… ;)

#!/bin/bash

# список чего-нить
list="mail.ru ya.ru google.ru" 

i=1

# функция обработки
test() {
  echo start $1
  ping -c 3 $1 > /dev/null
  echo end $1
}

# параметр - количество одновременных процессов
limit=$1
a=1
while true
do
  param=$(echo $list | awk "{print \$$a}")
  if [ $param ]
  then
# для того, чтобы убедиться
#	jobs
    jobs=$(jobs | wc -l)
    if (( jobs < limit))
	then
	  test $param &
	  (( a++ ))
# чтобы не было расхода процессора - раскомментируйте: (просто иначе на пингах не видно ;))
#	  sleep 1
	fi
  else
	break
  fi
done

wait
0
Fragster #
зы: i тан не нужен ;)
0
Fragster #
проверил на архивировании файлов — если тупо все в цикле запараллелить, без контроля количества процессов — чуть-чуть медленнее (5% примерно), чем 4 процесса с контролем количества.
0
Mezomish #
all: mail.ru ya.ru google.ru
 
%.ru:
    echo "start $@"; ping -c 3 $@ 1>/dev/null; echo "finish $@"
 

$ make -j 2

;)
0
Fragster #
прием с мэйком — хорош. но требует внешнего файла (ну, тут, конечно, можно и изгольнуться, но как-то не вижу с ходу). мой пример элементрарно переделывается под отсутствие внешнего файла — только на параметрах командной строки
0
Fragster #
«мы тут с мужиками подумали, и...»
в общем, изящный вариант, который на раз-два переделывается под командную строку:

#!/bin/bash

list="mail.ru fignya.ya ya.ru google.ru mista.ru"

test() {
  echo start $1
  ping -c 3 $1 > /dev/null
  echo end $1
}

limit=$1

func() {
  while [ $1 ]; do
    if (( `jobs  | wc -l` < limit )); then
      test $1 &
      shift
    fi
  done
}

# а если передавать все командной строкой - то shift;  func $@
func $list
wait
+1
Mezomish #
Шедевр. Честно.
Мои Вам хабрареспекты.
+4
cr0t #
Автор молодец! Использование стандартных средств (make) в нестандартных задачах (mencoder)! Умение мыслить широко и нестандартно — большой плюс для специалиста!
0
Vayun #
Замечательная тема, особенно приятно смотреть как ускоряет компиляцию чего-нибудь тяжелого make -j4 )
0
VlK #
Очаровательно.

А что там с xargs? Что дает по сравнению с make?
0
mikhailian #
На разные машинах можно распараллеливать с помьщью paexec, либо перейти на transcode, где соответствующая фича уже встроена.
0
Frosty #
Давно мучал подобный вопрос, имеется папка с туевой хучей картинок, надо все их прогнать через оптимизатор. Какое правило в этом случае составлять?
all: *.png
optipng -o7 $?
скармливает все файлы одному единственному процессу

all: *.png

%.png: %.png
optipng -o7 $<
получаем циклическое правило, которое make игнорирует.
0
Self_Perfection #
Выше же подсказали xargs -P (для меня тоже было открытием). Как-то так попробуйте:

$find -iname '*.png'|xargs -P 2 optipng -o7
0
Frosty #
cстранно, но что -P 2, что 4, что 0 всегда дают 2 процесса :/
0
ivanrt #
А так?
find -iname '*.png'|xargs -P4 -l1 optipng -o7

Мне кажется, что надо опцию -l1 дать, чтобы команда optipng запускалась каждый раз с одним файлом.
0
Frosty #
То, что доктор прописал. Блин, казалось бы 3 года в линукс системах, а только вчера узнал про такую полезную функцию xargs, сегодня открыл для себя mkfifo… Сколько лет нужно, чтобы узнать более менее все? :)
–9
und #
Виндузятникам (мне) это всё не понять. Столько мучаться чтобы сконвертировать пару файлов. Просто интересно, сколько времени вы сэкономили? Вы написали что «загружен только один процессор из двух, а это значит, что этот процесс можно ускорить, раза в два.» Сколько это в минутах, часах, днях? ;)

Главное что бы вам нравилось, а хабрасообщество судя по всему оценило вашу первую стастью на УРА.

+11
Frosty #
«Программист — это такой вид идиота, который потратит час, чтобы пятиминутную ручную работу за него за 3 секунды сделал компьютер» не помню от куда.
+2
develop7 #
Дада
точить топор? ты что, некогда! руби быстрее и баста!
тоже не помню, откуда.
0
z0rg #
пля, это про меня))))
+2
zw0rk #
Один помучался — решил, другие мучаться не будут и воспользуются готовым решением. Автор потенциально сэкономил кому-то некоторое количество времени.

Ну и сам себе тоже, если в будущем опять понадобится.
0
pomkaster #
А главное — получил удовольсвие от создания [полезной] штуки.
+4
develop7 #
Да и свечку вы тоже, небось, не держали. Может, у него 20 файлов, а может 20k.
И самое главное — ну не осчастливили Linux софтом типа «Super Duper Advanced Video Converter Pro» (всего за $24.99!!). Вот нам, несчастным, и приходится извращаться.
+4
und #
Да я что против чтоли? Я просто говорю что со стороны, тем кто не в курсе… это выглядит забавно.
+4
Frosty #
виндузятник (вы), упустили самое главное: это не топик о конвертировании видео, это топик о распараллеливании задач, а приведенный автором пример подойдет для множества случаев, где на выходе у параллелных процессов будут некие файлы.
Так то!
(с) К.О.
0
flaresun #
> Вы написали что «загружен только один процессор из двух, а это значит, что
> этот процесс можно ускорить, раза в два.»
> Сколько это в минутах, часах, днях? ;)


Всё зависит от кодируемых файлов — если они кодировались в один поток три часа, то в два потока с большой вероятностью перекодируются за полтора. Автор же написал «можно ускорить раза в два» ;)
0
und #
Спасибо Капитан. Мне было интересно, сколько ИМЕННО в этом случае автор экономит.

На мой вопрос ответил Frosty — habrahabr.ru/blogs/linux/66903/#comment_1885424
0
flaresun #
Да, не совсем понял суть вопроса. Просто после цитирования и просьбы уточнить «это в минутах, часах, днях» как-то «затерялся» предшествующий вопрос к автору…
Но судя по расширению файлов довольно большая вероятность, что там видео в HD, а так как конвертировать надо было не пару файлов, а побольше, то и экономия, думаю, довольно существенная. Даже если экономия была бы всего полчаса, это уже хорошо, так как на чтение манов этого достаточно, т.е. здесь во времени не теряем вовсе, а в будущем получаем существенную экономию.
В данном примере кодилось всё в 22 мегабитный поток, значит и видео не маленькое, и качество хотелось сохранить приемлемое. Попробуйте у себя на компе пару десятиминутных видеороликов сконвертировать с хорошим качеством, думаю, что экономия данного способа для бОльшего количества файлов сразу будет на лицо ;)
–1
und #
из 6 минутный mkv,H264 AVC 1280x720 25fps AC3 5.1ch 48kHz в avi,XviD 702x396 30fps (забыл поменять), 3mbps, MP3 160kbps

Конвертилось 12 минут, обя ядра на 92% заюзаны, температура до 74 поднялась.

Это если выстовить в проге приоритет 100%.

Ну и как сэкономить? :)
0
flaresun #
Ну если кодить для мобильника в 320x240 (т.е. получается в 320x180), то оно вообще закодируется за пару минут, а то и быстрее. В приведённом примере автора я не заметил ресайза видео и поток 22 мегабита вряд ли нужен для 720p, так что при тестировании Вы сильно упростили задачу взяв менее «тяжёлое» видео и закодировав в ещё более «лёгкое», чем источник. Кстати, Вы не указали параметры Motion precision, а они играют очень большую роль в скорости кодирования и качестве результата ;)
У меня на компе, для примера, в подобное разрешение (720x???) видео кодируется в худшем случае со скоростью один к одному (при сохранении хорошего качества), так что подобная перекодировка заняла бы максимум 6 минут, а скорее всего даже меньше.
Но ведь и при своём тесте Вы должны были увидеть экономию при многопоточности. У Вас один видеоролик кодировался 12 минут, т.е. при перекодировании на одном ядре этот процесс занял бы около 24 минут, значит Вы сэкономили 12 минут. У автора топика в примере восемь видеофайлов, значит если бы это были коротенькие видео 720p и кодировались бы для просмотра на обычном телевизоре, то на Вашем компьютере такое распараллеливание сэкономило бы полтора часа как минимум. Считайте, что Вы сами ответили на свой вопрос.
+2
ivanrt #
Экономия на времени кодирования получилась где-то в 2 часа, скрипты писал минут 10 вспоминая суфиксные правила GNU make + часа два на написании статьи и общее время проведеное на хабр.
+1
INSTE #
В mencoder XviD и x264 уже поддерживают многопоточность искаропки.
man mencoder:

xvidencopts
The following option is only available in the 1.2.x version of Xvid.

threads=
Create n threads to run the motion estimation (default: 0). The maximum number
of threads that can be used is the picture height divided by 16.

x264encopts
threads=
Spawn threads to encode in parallel on multiple CPUs (default: 1). This has a
slight penalty to compression quality. 0 or 'auto' tells x264 to detect how
many CPUs you have and pick an appropriate number of threads.
0
INSTE #
ffmpeg кстати нет, а жаль.
0
tipok #
по умолчанию — нет, а если собрать руками и указать ключик --enable-pthreads то будет.
0
INSTE #
Ffmpeg нужно ставить отдельно, или подойдет тот, что в комплекте исходников mplayer?
0
tipok #
не, я про отдельный, а тот что в мплеере — нужно флаги смотреть.
0
INSTE #
Такой способ, к сожалению, подходит лишь тем, кто крепко держит в руках консоль и svn. :)
0
7hr33pw1r3 #
В Gentoo ебилд ffmpeg'а имеет USE-флаг threads который добавляет этот самый --enable-pthreads, также есть ебилд с который сам достает самое свежее из svn.
0
sovok_kpss #
Еще помнится, что с помощью make iso-образы можно весело создавать
+3
Bkmz #
Интересно бы узнать ;)
Автор, а не напишите ли хабратопик на эту тему???)
0
vk2 #
make -j — отличная вещь :)
0
adontz #
Статья хорошая, просто хотелось отметить, что перекодирование видео должно скорее в скорость записи на винчестер упереться, чем в процессор. В смысле загрузить два процессора на полную скорее всего уже не выйдет.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
+1
INSTE #
Смотря во что кодировать.
Если в h.264 с тонной ресурсоемких опций, то скорость обработки более 10 fps/ядро получить трудно.
0
a5b #
Если задачи работают примерно одинаковое время, можно вручную раскидать их по ядрам при помощи утилиты taskset.
0
Andrewus #
Во-первых, спасибо за пост, полезно, интересно и юзабельно.

Правда, то ли у меня другая сборка mencoder, то ли по-другому настроена система, но оба ядра Turion 64 загружены на 100% еще с первого способа:)
Так что полезным оказалось скорее скармливание списка файлов.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.