LINUX.ORG.RU

семафоры для bash?


0

0

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

Есть простой способ распараллелить их на нужное количество потоков на bash/shell?
Перемещено cavia_porcellus из Talks

★★★★

Перенесите, плиз, в Development.

Davidov ★★★★
() автор топика

Проще не баш использовать, а мейкфайлы сделать через cmake например.

anotheranonymous
()
Ответ на: комментарий от tailgunner

И как здесь контролируется количество потоков? Мне нужно по количеству процессоров - 8.

Сейчас сделал просто lock file на каждую команду и запустил 8 скриптов одновременно, пусть соревнуются. Но должен быть способ проще.

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

> И как здесь контролируется количество потоков?

Процессов, не нитей. И оно никак не контролируется..

> Мне нужно по количеству процессоров - 8.

Я сильно сомневаюсь, что оно тебе нужно, поскольку планирование лучше оставить ядру... ОК, ты хочешь поддерживать 8 процессов запущенными постоянно, один завершился - другой запущен?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>Процессов, не нитей

Оговорился.

>ты хочешь поддерживать 8 процессов запущенными постоянно, один завершился - другой запущен?

Да. Ресурсоёмкие задачки.

Davidov ★★★★
() автор топика

Попробуй обрабатывать SIGCHLD, но вообще это задача для GNU Make. Или, если устраивает эффективность на 90%, можно попробовать запускать задачи пачками по 8 штук и запускать каждую пачку только после окончания предыдущей.

AEP ★★★★★
()
Ответ на: комментарий от AEP

> Попробуй обрабатывать SIGCHLD,

Не обрабатывается почему-то :/

> запускать задачи пачками по 8 штук и запускать каждую пачку только после окончания предыдущей.

Это неспортивно :)

tailgunner ★★★★★
()
Ответ на: комментарий от AEP

>GNU Make

Надо попробовать. Нужно писать как-то вроде: all: task1 task2 .. taskN и запускать с -j8, да?

Команд, кстати около миллиона. Текущая реализация с flock, вроде бы, создаёт большую нагрузку на fs; хотя м.б. проблема не с flock.

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

>>ты хочешь поддерживать 8 процессов запущенными постоянно, один завершился - другой запущен?

>Да. Ресурсоёмкие задачки.

запускай сразу все. Только первым 8-и nice - 0, следующей восьмерке nice - 1 и т.д.

dikiy ★★☆☆☆
()
Ответ на: комментарий от Davidov

>Команд, кстати около миллиона.

ладно. мой предыдущий пост считать недействительным )

dikiy ★★☆☆☆
()
Ответ на: комментарий от tailgunner

Как выяснилось, SIGCHLD таки можно обрабатывать, но толку от этого нет...

Вот вариант без сигналов и прочей магии, он грязноватый, но идею иллюстрирует:

cmds="
/bin/sleep 1\n
/bin/sleep 1\n
/bin/sleep 1\n
/bin/sleep 1\n
/bin/sleep 7\n
/bin/sleep 8\n
/bin/sleep 9\n
/bin/sleep 10\n
/bin/sleep 11; echo Типа всё\n"

# команды передаются на стандартный вввод
echo -e $cmds | \
(MAXPROCS=8
 nalive=$MAXPROCS
 eof="false"
 nextcmd() {
   local c
   read c
   if [ -z "$c" ]; then
     eval $eof || echo No more commands to run
     eof="true"
   else
     echo Adding $c
     eval "$c; echo -n x >exits" &
     nalive=$[nalive + 1]
   fi
 }
 get_exits() {
   local x
   x=$(cat exits)
   echo $[$(echo "$x" | wc -c) - 1]
 }
 # создаем FIFO, через которое сообщается о завершении команд
 mkfifo exits
 # пускаем первые MAXPROCS команд
 echo Priming...
 for n in $(seq 1 $MAXPROCS); do
   read c
   echo $c
   eval "$c; echo -n x >exits" &
 done
 # ждем сообщения о завершении команд, и запускаем им на замену новые
 while :; do
   nalive=$[$nalive - $(get_exits)]
   for i in $(seq $nalive $[$MAXPROCS - 1]); do
     nextcmd
   done
   eval $eof && break
 done
 echo Waiting for $nalive unfinished processes...
 while :; do
   nalive=$[$nalive - $(get_exits)]
   [ $nalive -eq 0 ] && break
   echo $nalive left
 done
 wait
 rm exits)

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Спасибо. Вообще кошмар, лучше такое на питоне писать, наверное ;)


sem = threading.Semaphore(8)

for cmd in commands:
  sem.acquire()
  thread.start_new_thread(call_cmd, (cmd,))

Davidov ★★★★
() автор топика

cmd1 < /dev/null > /dev/null | cmd2 < /dev/null > /dev/null | cmd3 < /dev/null > /dev/null | cmd4 < /dev/null > /dev/null | cmd5 < /dev/null > /dev/null | cmd6 < /dev/null > /dev/null | cmd7 < /dev/null > /dev/null | cmd8 < /dev/null > /dev/null

p.s. ответ автосгенерирован:)

dilmah ★★★★★
()
Ответ на: комментарий от dilmah

Так не об этом же речь!

Ну вот например:

for f1 in filelist; do
  for f2 in filelist; do
    if [[ "$f1" < "$f2" ]]; then
      diff $f1 $f2 > diff_$f1_$f2.txt
    fi
  done
done

Допустим filelist - это 800 файлов. На машине 8 процессоров; хочется, чтобы одновременно всегда выполнялись 8 diff'ов.

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

раз в секунду проверяешь скока диффов запущено и плодишь новые если их не хватает. Как вариант можно отслеживать статус чайлдов.

И не обязательно на баше такое писать, вполне можно и python/perl.

true_admin ★★★★★
()
Ответ на: комментарий от Davidov

> Допустим filelist - это 800 файлов. На машине 8 процессоров; хочется, чтобы одновременно всегда выполнялись 8 diff'ов.

я ж их в пайп объединил. Пайпы исполняются последовательно только в мс-дос

dilmah ★★★★★
()
Ответ на: комментарий от Davidov

> Вообще кошмар

Кошмаром было это писАть %) Шелл приспособлен для обработки потоков данных,
а не для планирования.

> for cmd in commands:

> sem.acquire()

> thread.start_new_thread(call_cmd, (cmd,))


Ну это же не вся прога...

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>Ну это же не вся прога...

import thread
import threading
import subprocess

def call_cmd(cmd):
    subprocess.call(cmd.split())
    sem.release()

commands = ['sleep 10', 'sleep 5', 'sleep 12'] * 20

sem = threading.Semaphore(8)

for cmd in commands:
    sem.acquire()
    thread.start_new_thread(call_cmd, (cmd,))

Davidov ★★★★
() автор топика

А может запускать скрипт под отдельным пользователем, у которого ограничено количество процессов (ulimit -u)?

Это конечно, не идеальное решение, поскольку два таких скрипта не запустишь.

Не, это совсем плохое решение, ибо скрипт будет завершаться с ошибкой при попытке форкнуться.

gorilych ★★
()
Ответ на: комментарий от dilmah

>Пайпы исполняются последовательно только в мс-дос

Этот вариант работает только пачками по восемь, и не понимает, что одна команда может завершиться раньше; и в таком случае нужно сразу запускать следующую.

Davidov ★★★★
() автор топика
Ответ на: комментарий от tailgunner

Погоди, а разве оно не будет постоянно крутиться в цикле?

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

> commands = ['sleep 10', 'sleep 5', 'sleep 12'] * 20

> for cmd in commands:

Ну ты ж не станешь весь миллион команд держать во внутреннем массиве? :)

> Погоди, а разве оно не будет постоянно крутиться в цикле?

Вторая часть конвейера - это автономная прога, которая читает команды для исполнения с stdin. Там встроен цикл, да. Проверка на EOF, все дела.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>Ну ты ж не станешь весь миллион команд держать во внутреннем массиве? :)

Во-первых, могу :) Во-вторых, можно сделать файл с командами. В-третьих, на самом деле команды выполняются для каждого файла в директории (то есть эдакий многопоточный find -exec). И, в-четвёртых, в твоём примере это тоже опущено :)

Davidov ★★★★
() автор топика
Ответ на: комментарий от gorilych

>вот здесь решение подобной задачи:

О... спасибо.

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

> И, в-четвёртых, в твоём примере это тоже опущено :)

ненене, в моем примере всё честно читается со stdin. Я веду к тому, что на Питоне прога вряд ли будет более чем в 2 раза короче (хотя будет яснее, конечно).

tailgunner ★★★★★
()
Ответ на: комментарий от gorilych

> вот здесь решение подобной задачи:

Вроде оно заканчивается на "I am pretty much stuck at where to begin next" и совете использовать Питон? %)

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

import thread
import threading
import subprocess
import sys

def call_cmd(cmd):
    subprocess.call(cmd.split())
    sem.release()

sem = threading.Semaphore(8)

for cmd in sys.stdin:
    sem.acquire()
    thread.start_new_thread(call_cmd, (cmd,))

Кажется, это более, чем в 2 раза )

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

> Кажется, это более, чем в 2 раза )

В общем да, но оно при старте читает весь миллион команд :) Который, теоретически, может быть еще не весь готов (ага, я придираюсь).

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

В последнем приведённом варианте из stdin читается; и, вроде, не весь миллион а построчно :)

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

import thread, threading, subprocess, sys

sem = threading.Semaphore(8)

for cmd in sys.stdin:
    sem.acquire()
    thread.start_new_thread(lambda:(subprocess.call(cmd.split()), sem.release()), ())


Уже почти похоже на перл ;) Интересно, порядок вычисления аргументов в списке фиксированный? Или есть другой способ имитации сишного оператора запятая?  

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

>В последнем приведённом варианте из stdin читается; и, вроде, не весь миллион а построчно :)

Коонструкция for cmd in sys.stdin читает весь stdin до EOF, и потом выдает прочитанное построчно (python 2.5.2 из Lenny).

> порядок вычисления аргументов в списке фиксированный? Или есть другой способ имитации сишного оператора запятая?

Не надо устраивать OPCC :)

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

может проще написать коротенькую программку на С которая будет работать с SysV семафорами? и вызывать ее из скрипта.. типа sem $KEY $OPERATION ?

OxiD ★★★★
()
Ответ на: комментарий от OxiD

> Может проще написать коротенькую программку на С которая будет работать с SysV семафорами?

Нет. Проще - на Питоне. Скрипт, который я написал - это курьез. Он работает, но в нем наверняка куча проблем с escaping'ом, он довольно длинный, с неочевидными вызовами... в общем, решить задачу на шелле можно, но только в крайнем случае.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

я не знаток питона но мне кажется что его семофоры это posix кототорые работают только для потоков, учитывая все что вы сами пишете про возможные проблемы, наверное проще написать короткую утилитку на Ц. Даже писать не надо она уже есть в примерах, кажется у стивенса. Плюс питон не входит в LSB, перл который входит, обычно собран без тредов и придется опять юзать сис5.. + те же проблемы с эскейпингом... вообщем не знаю, но мне кажется мой подход проще и лучше вписывается в систему полностью реализованную на баше.

OxiD ★★★★
()
Ответ на: комментарий от OxiD

> мне кажется что его семофоры это posix кототорые работают только для потоков

Да, но это неважно в данном случае. Да и по-любому есть модуль os, в нем - нормальные spawn и wait, которых нет в шелле.

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

За исключением того, что она не полностью на баше.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

за исключением того что бОльшая часть возможностей баш написана на С

//ни надо флейма, это шутко, я просто высказал точку зрения :)

OxiD ★★★★
()

а, ну если по теме, но на чистом шелле можно иметь семафоры и прочую синхронизацию -- с помощью mv(1)

например, спинлок:

  while ! mv $lock_file ${lock_file}.locked 2> /dev/null; do
    true
  done

dilmah ★★★★★
()
Ответ на: комментарий от tailgunner

>Коонструкция for cmd in sys.stdin читает весь stdin до EOF, и потом выдает прочитанное построчно (python 2.5.2 из Lenny).

Там что-то более хитрое. Попытка подать 100-мегабайтный файл на вход скрипта с таким циклом не показала увеличения занимаемой памяти.

Davidov ★★★★
() автор топика
Ответ на: комментарий от dilmah

А чем mv лучше flock? И, честно говоря, я не совсем понимаю, как сделать семафор со значением 8, например, при помощи mv.

Davidov ★★★★
() автор топика
Ответ на: комментарий от tailgunner

>Нет. Проще - на Питоне. Скрипт, который я написал - это курьез. Он работает, но в нем наверняка куча проблем с escaping'ом, он довольно длинный, с неочевидными вызовами... в общем, решить задачу на шелле можно, но только в крайнем случае.

Кстати, как выяснилось, приведённый мной скрипт работает неправильно :)

http://bugs.python.org/issue1731717

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

>http://bugs.python.org/issue1731717

Я не уверен, что это именно этот баг; но похоже subprocess.call не thread-safe; вместо двух разных команд иногда одна выполняется два раза.

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

> А чем mv лучше flock?

тем что mv это позикс:) В BSD вместо flock shlock.

dilmah ★★★★★
()
Ответ на: комментарий от tailgunner

кстати ту программку о которой я говорил можно написать на питоне с помощью модуля os :)

OxiD ★★★★
()
Ответ на: комментарий от www_linux_org_ru

>я так и не понял, почему от make -j8 отказались.

Да не отказались. Хотя я думаю, make тоже загрузит все 1M команд в память. Наверное, это не очень хорошо.

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

>>Коонструкция for cmd in sys.stdin читает весь stdin до EOF, и потом выдает прочитанное построчно (python 2.5.2 из Lenny).

>Там что-то более хитрое. Попытка подать 100-мегабайтный файл на вход скрипта с таким циклом не показала увеличения занимаемой памяти.

Там итератор и эта конструкция действительно читает по одной строке. А у tailgunner'а в Lenny странный питон.

const86 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.