กระจาย stdin ไปยังกระบวนการแบบขนาน


13

ฉันมีงานที่ประมวลผลรายการไฟล์ใน stdin เวลาเริ่มต้นของโปรแกรมมีความสำคัญและระยะเวลาที่แต่ละไฟล์ใช้กันอย่างกว้างขวาง ฉันต้องการที่จะวางไข่เป็นจำนวนมากในกระบวนการเหล่านี้จากนั้นส่งงานไปยังที่ใดก็ตามที่ไม่ยุ่ง มีเครื่องมือ commandline ที่แตกต่างกันหลายอย่างที่เกือบจะทำในสิ่งที่ฉันต้องการฉัน จำกัด ให้เหลือสองตัวเลือกที่ใช้งานได้

find . -type f | split -n r/24 -u --filter="myjob"
find . -type f | parallel --pipe -u -l 1 myjob

ปัญหาก็คือsplitการปัดเศษบริสุทธิ์ดังนั้นหนึ่งในกระบวนการที่ได้รับข้างหลังและอยู่ข้างหลังล่าช้าการดำเนินการทั้งหมดเสร็จสมบูรณ์; ในขณะที่parallelต้องการวางไข่หนึ่งกระบวนการต่อ N บรรทัดหรือไบต์ของอินพุตและฉันปิดท้ายการใช้จ่ายเวลามากเกินไปในการเริ่มต้นค่าใช้จ่าย

มีบางอย่างเช่นนี้ที่จะใช้กระบวนการและฟีดบรรทัดไปยังกระบวนการใดก็ตามที่มี stdins ที่ไม่ถูกบล็อก


splitคำสั่งนั้นมาจากไหน? ชื่อขัดแย้งกับยูทิลิตี้การประมวลผลข้อความมาตรฐาน
Gilles 'หยุดความชั่วร้าย'

@Gilles ก็ GNU หนึ่ง: "แยก (coreutils GNU) 8.13" การใช้มันเป็นทางเลือกแปลก ๆ สำหรับ xargs อาจไม่ใช่การใช้ที่ตั้งใจ แต่มันใกล้เคียงกับสิ่งที่ฉันต้องการมากที่สุด
BCoates

2
ฉันกำลังคิดเกี่ยวกับสิ่งนั้นและปัญหาพื้นฐานคือรู้ว่าตัวอย่างของmyjobพร้อมที่จะรับข้อมูลเพิ่มเติม ไม่มีวิธีที่จะรู้ว่าโปรแกรมพร้อมที่จะประมวลผลอินพุตมากขึ้นสิ่งที่คุณสามารถรู้ได้คือบัฟเฟอร์บางแห่ง (บัฟเฟอร์ไพพ์, บัฟเฟอร์ stdio) พร้อมที่จะรับอินพุตเพิ่มขึ้น คุณสามารถจัดโปรแกรมให้ส่งคำขอบางประเภท (เช่นแสดงพรอมต์) เมื่อพร้อมหรือไม่
Gilles 'หยุดความชั่วร้าย'

สมมติว่าโปรแกรมไม่ได้ใช้งานการเรียกใช้บน stdin ระบบไฟล์ FUSE ที่ตอบสนองต่อการreadโทรจะทำเคล็ดลับ นั่นเป็นความพยายามเขียนโปรแกรมที่ค่อนข้างใหญ่
Gilles 'หยุดความชั่วร้าย'

ทำไมคุณใช้-l 1ในparallelargs? IIRC ที่บอกขนานกับการประมวลผลอินพุตหนึ่งบรรทัดต่องาน (เช่นหนึ่งชื่อไฟล์ต่อทางแยกของ myjob ดังนั้นค่าใช้จ่ายในการเริ่มต้นจำนวนมาก)
cas

คำตอบ:


1

ที่ดูไม่เป็นไปได้ในกรณีทั่วไป มันบอกเป็นนัยว่าคุณมีบัฟเฟอร์สำหรับแต่ละกระบวนการและคุณสามารถดูบัฟเฟอร์จากภายนอกเพื่อตัดสินใจว่าจะใส่รายการถัดไป (การตั้งเวลา) ... แน่นอนคุณอาจเขียนอะไรบางอย่าง (หรือใช้ระบบแบทช์เช่น slurm)

แต่ขึ้นอยู่กับว่ากระบวนการคืออะไรคุณอาจสามารถประมวลผลอินพุตล่วงหน้าได้ ตัวอย่างเช่นหากคุณต้องการดาวน์โหลดไฟล์อัปเดตรายการจากฐานข้อมูลหรือสิ่งที่คล้ายกัน แต่ 50% ของข้อมูลทั้งหมดจะถูกข้ามไป (และหากคุณมีความแตกต่างในการประมวลผลขนาดใหญ่ขึ้นอยู่กับอินพุต) จากนั้นเพียงติดตั้งโปรเซสเซอร์ล่วงหน้า ที่ตรวจสอบว่ารายการใดจะใช้เวลานาน (มีไฟล์อยู่ข้อมูลถูกเปลี่ยนแปลง ฯลฯ ) ดังนั้นสิ่งใดก็ตามที่มาจากอีกด้านหนึ่งคือการรับประกันว่าจะใช้เวลาค่อนข้างเท่ากัน แม้ว่าฮิวริสติกจะไม่สมบูรณ์แบบคุณอาจท้ายด้วยการปรับปรุงที่สำคัญ คุณอาจดัมพ์ผู้อื่นไปยังไฟล์และประมวลผลในภายหลังในลักษณะเดียวกัน

แต่ขึ้นอยู่กับกรณีการใช้งานของคุณ


1

ไม่ไม่มีทางออกทั่วไป โปรแกรมเลือกจ่ายงานของคุณจำเป็นต้องรู้เมื่อแต่ละโปรแกรมพร้อมที่จะอ่านบรรทัดอื่นและไม่มีมาตรฐานที่ฉันทราบซึ่งอนุญาตให้ทำเช่นนั้น สิ่งที่คุณสามารถทำได้คือวางสายบน STDOUT และรอสิ่งที่จะกินมัน ไม่มีวิธีที่ดีจริง ๆ สำหรับผู้ผลิตในท่อเพื่อบอกว่าผู้บริโภครายต่อไปพร้อมหรือไม่


0

ฉันไม่คิดอย่างนั้น ในนิตยสารเล่มโปรดของฉันคือบทความหนึ่งครั้งเกี่ยวกับการเขียนโปรแกรม bash ซึ่งทำในสิ่งที่คุณต้องการ ฉันยินดีที่จะเชื่อว่าหากมีเครื่องมือในการทำเช่นนั้นพวกเขาจะได้กล่าวถึงพวกเขา ดังนั้นคุณต้องการบางสิ่งบางอย่างตาม:

set -m # enable job control
max_processes=8
concurrent_processes=0

child_has_ended() { concurrent_processes=$((concurrent_processes - 1)) }

trap child_has_ended SIGCHLD # that's magic calling our bash function when a child processes ends

for i in $(find . -type f)
do
  # don't do anything while there are max_processes running
  while [ ${concurrent_processes} -ge ${max_processes}]; do sleep 0.5; done 
  # increase the counter
  concurrent_processes=$((concurrent_processes + 1))
  # start a child process to actually deal with one file
  /path/to/script/to/handle/one/file $i &
done

เห็นได้ชัดว่าคุณอาจเปลี่ยนคำเรียกร้องให้เป็นสคริปต์การทำงานจริงตามที่คุณต้องการ นิตยสารที่ฉันพูดถึงตอนแรกทำสิ่งต่าง ๆ เช่นตั้งค่าท่อและเริ่มหัวข้อคนงาน ลองดูmkfifoว่า แต่เส้นทางนั้นซับซ้อนกว่ามากเนื่องจากกระบวนการของผู้ปฏิบัติงานจำเป็นต้องส่งสัญญาณกระบวนการหลักที่พวกเขาพร้อมที่จะรับข้อมูลเพิ่มเติม ดังนั้นคุณต้องมีหนึ่งฟีเจอร์สำหรับกระบวนการของผู้ปฏิบัติงานแต่ละคนเพื่อส่งข้อมูลและอีกหนึ่งฟีเจอร์สำหรับกระบวนการหลักเพื่อรับข้อมูลจากคนงาน

การปฏิเสธความรับผิด ฉันเขียนสคริปต์นั้นจากด้านบนของหัวของฉัน อาจมีปัญหาเกี่ยวกับไวยากรณ์


1
ดูเหมือนจะไม่เป็นไปตามข้อกำหนด: คุณกำลังเริ่มต้นอินสแตนซ์ที่แตกต่างกันของโปรแกรมสำหรับแต่ละรายการ
Gilles 'หยุดความชั่วร้าย'

ก็มักจะนิยมใช้มากกว่าfind . -type f | while read i for i in $(find . -type f)

0

สำหรับ GNU Parallel คุณสามารถตั้งขนาดบล็อกโดยใช้ --block อย่างไรก็ตามคุณจำเป็นต้องมีหน่วยความจำเพียงพอที่จะเก็บ 1 บล็อกไว้ในหน่วยความจำสำหรับกระบวนการที่ทำงานอยู่แต่ละกระบวนการ

ฉันเข้าใจว่านี่ไม่ใช่สิ่งที่คุณต้องการอย่างแน่นอน แต่ตอนนี้อาจเป็นการแก้ไขที่ยอมรับได้

หากงานของคุณโดยเฉลี่ยใช้เวลาเดียวกันคุณอาจใช้ mbuffer:

find . -type f | split -n r/24 -u --filter="mbuffer -m 2G | myjob"

0

ลองสิ่งนี้:

mkfifo สำหรับแต่ละกระบวนการ

จากนั้นแขวนtail -f | myjobบนแต่ละ Fifo

ตัวอย่างเช่นการตั้งค่าคนงาน (กระบวนการ myjob)

mkdir /tmp/jobs
for X in 1 2 3 4
do
   mkfifo pipe$X
   tail -f pipe$X | myjob &
   jobs -l| awk '/pipe'$X'/ {print $2, "'pipe$X'"}' >> pipe-job-mapping
done

ขึ้นอยู่กับแอปพลิเคชันของคุณ (myjob) คุณอาจจะสามารถใช้งาน -s เพื่อหางานที่หยุด มิฉะนั้นจะแสดงรายการกระบวนการที่เรียงลำดับตาม CPU และเลือกทรัพยากรที่น้อยที่สุด ของการมีรายงานงานของตัวเองเช่นโดยการตั้งค่าสถานะในระบบไฟล์เมื่อมันต้องการงานมากขึ้น

สมมติว่างานหยุดลงเมื่อรออินพุตให้ใช้

jobs -sl เพื่อหา pid ของงานที่หยุดทำงานและกำหนดให้มันทำงานเช่น

grep "^$STOPPED_PID" pipe-to-job-mapping | while read PID PIPE
do
   cat workset > $PIPE
done

ฉันทดสอบสิ่งนี้ด้วย

garfield:~$ cd /tmp
garfield:/tmp$ mkfifo f1
garfield:/tmp$ mkfifo f2
garfield:/tmp$ tail -f f1 | sed 's/^/1 /' &
[1] 21056
garfield:/tmp$ tail -f f2 | sed 's/^/2 /' &
[2] 21058
garfield:/tmp$ echo hello > f1
1 hello
garfield:/tmp$ echo what > f2
2 what
garfield:/tmp$ echo yes > f1
1 yes

สิ่งนี้ฉันต้องยอมรับว่าเพิ่งปรุงแต่งดังนั้น ymmv


0

สิ่งที่จำเป็นจริงๆในการแก้ไขปัญหานี้คือกลไกของคิวบางประเภท

เป็นไปได้หรือไม่ที่จะให้งานอ่านอินพุตจากคิวเช่นคิวข้อความ SYSV จากนั้นให้โปรแกรมรันโดยขนานเพียงแค่กดค่าลงในคิว

ความเป็นไปได้อีกอย่างคือการใช้ไดเรกทอรีสำหรับคิวเช่นนี้

  1. ผลลัพธ์การค้นหาสร้าง symlink ให้กับแต่ละไฟล์เพื่อประมวลผลในไดเรกทอรี pending
  2. กระบวนการแต่ละงานดำเนินการmvของไฟล์แรกที่เห็นในไดเรกทอรีไปยังไดเรกทอรีพี่น้องของชื่อpendinginprogress
  3. หากงานย้ายไฟล์ได้สำเร็จก็จะทำการประมวลผล ไม่เช่นนั้นจะกลับไปค้นหาและย้ายชื่อไฟล์อื่นจากpending

0

การอธิบายคำตอบของ @ ash คุณสามารถใช้คิวข้อความ SYSV เพื่อแจกจ่ายงาน หากคุณไม่ต้องการเขียนโปรแกรมของคุณเองใน C มีโปรแกรมอรรถประโยชน์ที่เรียกipcmdว่าสามารถช่วยได้ นี่คือสิ่งที่ผมใส่กันที่จะผ่านการส่งออกของfind $DIRECTORY -type fไป$PARALLELจำนวนของกระบวนการ:

set -o errexit
set -o nounset

export IPCMD_MSQID=$(ipcmd msgget)

DIRECTORY=$1
PARALLEL=$2

# clean up message queue on exit
trap 'ipcrm -q $IPCMD_MSQID' EXIT

for i in $(seq $PARALLEL); do
   {
      while true
      do
          message=$(ipcmd msgrcv) || exit
          [ -f $message ] || break
          sleep $((RANDOM/3000))
      done
   } &
done

find "$DIRECTORY" -type f | xargs ipcmd msgsnd

for i in $(seq $PARALLEL); do
   ipcmd msgsnd "/dev/null/bar"
done
wait

นี่คือการทดสอบการทำงาน:

$ for i in $(seq 20 10 100) ; do time parallel.sh /usr/lib/ $i ; done
parallel.sh /usr/lib/ $i  0.30s user 0.67s system 0% cpu 1:57.23 total
parallel.sh /usr/lib/ $i  0.28s user 0.69s system 1% cpu 1:09.58 total
parallel.sh /usr/lib/ $i  0.19s user 0.80s system 1% cpu 1:05.29 total
parallel.sh /usr/lib/ $i  0.29s user 0.73s system 2% cpu 44.417 total
parallel.sh /usr/lib/ $i  0.25s user 0.80s system 2% cpu 37.353 total
parallel.sh /usr/lib/ $i  0.21s user 0.85s system 3% cpu 32.354 total
parallel.sh /usr/lib/ $i  0.30s user 0.82s system 3% cpu 28.542 total
parallel.sh /usr/lib/ $i  0.27s user 0.88s system 3% cpu 30.219 total
parallel.sh /usr/lib/ $i  0.34s user 0.84s system 4% cpu 26.535 total

0

ถ้าคุณสามารถประเมินได้ว่านานแฟ้มใส่โดยเฉพาะอย่างยิ่งจะได้รับการประมวลผลและกระบวนการปฏิบัติงานที่ไม่ได้มีวิธีการที่จะรายงานกลับไปจัดตารางเวลา (ตามที่พวกเขาทำในสถานการณ์การคำนวณแบบขนานปกติ - มักจะผ่านMPI ), คุณโดยทั่วไปมักจะออกจากโชค - จ่ายค่าปรับของคนงานบางคนในการประมวลผลอินพุตนานกว่าคนอื่น (เพราะความไม่เท่าเทียมกันของการป้อนข้อมูล) หรือจ่ายค่าปรับจากการวางไข่กระบวนการใหม่เดียวสำหรับไฟล์อินพุตทุกไฟล์


0

GNU Parallel มีการเปลี่ยนแปลงใน 7 ปีที่ผ่านมา ดังนั้นวันนี้มันสามารถทำได้:

ตัวอย่างนี้แสดงให้เห็นว่ามีการให้บล็อกเพิ่มเติมในกระบวนการ 11 และ 10 มากกว่ากระบวนการ 4 และ 5 เนื่องจาก 4 และ 5 อ่านช้ากว่า:

seq 1000000 |
  parallel -j8 --tag --roundrobin --pipe --block 1k 'pv -qL {}0000 | wc' ::: 11 4 5 6 9 8 7 10
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.