การใช้ขนานเพื่อประมวลผลไฟล์อินพุตเฉพาะไปยังไฟล์เอาต์พุตที่ไม่ซ้ำกัน


18

ฉันมีปัญหาการเขียนสคริปต์เชลล์ซึ่งฉันได้รับไดเรกทอรีที่เต็มไปด้วยไฟล์อินพุต (ไฟล์แต่ละไฟล์มีหลายบรรทัดอินพุต) และฉันจำเป็นต้องประมวลผลเป็นรายบุคคลโดยเปลี่ยนเส้นทางผลลัพธ์แต่ละไฟล์ไปเป็นไฟล์ที่ไม่ซ้ำกัน (aka, file_1.input ที่จะถูกจับใน file_1.output และอื่น ๆ )

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

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

ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชม!

ตัวอย่างไดเรกทอรีอินพุต:

> ls -l input_files/
total 13355
location1.txt
location2.txt
location3.txt
location4.txt
location5.txt

สคริปต์:

> cat proces_script.sh
#!/bin/sh

customScript -c 33 -I -file [inputFile] -a -v 55 > [outputFile]

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

แทนที่จะใช้กระบวนการทั้งหมดของฉันฉันคิดว่าจะเริ่มต้นด้วยหลักฐานคำสั่งแนวคิดเพื่อพิสูจน์วิธีแก้ปัญหาของเขาในสภาพแวดล้อมของฉัน ดูการใช้งานที่แตกต่างกันสองแบบของฉัน (และบันทึก):

find /home/me/input_files -type f -name *.txt | parallel cat /home/me/input_files/{} '>' /home/me/output_files/{.}.out

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

parallel cat '>' /home/me/output_files/{.}.out :::  /home/me/input_files/*

โซลูชันที่สองนี้ใช้กระบวนทัศน์ตัวแปรอินพุตของขนานเพื่ออ่านไฟล์ในอย่างไรก็ตามสำหรับมือใหม่สิ่งนี้ทำให้เกิดความสับสนมากขึ้น สำหรับฉันแล้วการใช้ find a และ pipe นั้นตรงกับความต้องการของฉัน

คำตอบ:


27

GNU Parallel ได้รับการออกแบบสำหรับงานประเภทนี้:

parallel customScript -c 33 -I -file {} -a -v 55 '>' {.}.output ::: *.input

หรือ:

ls | parallel customScript -c 33 -I -file {} -a -v 55 '>' {.}.output

มันจะทำงานหนึ่งงานต่อซีพียูคอร์

คุณสามารถติดตั้ง GNU Parallel ได้ง่ายๆโดย:

wget https://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

ดูวิดีโอแนะนำสำหรับ GNU Parallel เพื่อเรียนรู้เพิ่มเติม: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


คำตอบที่ดี (และประเด็นสำคัญสำหรับการอ่านคำขอของฉันในการใช้ขนาน)
J Jones

5

วิธีมาตรฐานในการทำเช่นนี้คือการตั้งค่าคิวและวางไข่จำนวนคนงานที่รู้วิธีดึงบางสิ่งออกจากคิวและประมวลผล คุณสามารถใช้ fifo (หรือชื่อไปป์) สำหรับการสื่อสารระหว่างกระบวนการเหล่านี้

ด้านล่างเป็นตัวอย่างที่ไร้เดียงสาเพื่อแสดงแนวคิด

สคริปต์คิวง่าย ๆ :

#!/bin/sh
mkfifo /tmp/location-queue
for i in inputfiles/*; do
  echo $i > /tmp/location-queue
done
rm /tmp/location-queue

และคนงาน:

#!/bin/sh
while read file < /tmp/location-queue; do
  process_file "$file"
done

process_file อาจถูกกำหนดไว้ที่ไหนสักแห่งในพนักงานของคุณและมันสามารถทำอะไรก็ได้ที่คุณต้องการ

เมื่อคุณมีสองชิ้นเหล่านี้คุณสามารถมีจอภาพแบบง่าย ๆ ที่เริ่มกระบวนการคิวและกระบวนการทำงานจำนวนเท่าใดก็ได้

สคริปต์การตรวจสอบ:

#!/bin/sh
queue.sh &
num_workers="$1"
i=0
while [ $i < $num_workers ]; do
  worker.sh &
  echo $! >> /tmp/worker.pids
  i=$((i+1))
done
monitor_workers

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


จอภาพฉลาดพอที่จะหยุดการวางไข่กับคนงานใหม่จนกว่าจะเสร็จงานต่อไปหรือไม่ ($ $ เคยลดลง) ---- ตอบการแก้ไขของฉันเองคนงานไม่เคยหายไปพวกเขาเพียงแค่ประมวลผลไฟล์จนกว่าการประมวลผลทั้งหมดจะหมดไป
J Jones

บรรทัด "monitor_workers" ในตอนท้ายของสคริปต์การตรวจสอบคืออะไร
J Jones

@Jones - monitor_workersเป็นเหมือนprocess_file- มันเป็นฟังก์ชั่นที่ทำสิ่งที่คุณต้องการ เกี่ยวกับจอภาพ - คุณพูดถูก ควรบันทึก pids ของผู้ปฏิบัติงาน (เพื่อให้สามารถส่งสัญญาณ kill) และตัวนับต้องเพิ่มขึ้นเมื่อเริ่มทำงาน ฉันได้แก้ไขคำตอบเพื่อรวมสิ่งนั้นแล้ว
Shawn J. Goff

ผมขอขอบคุณการทำงานของคุณ แต่ฉันคิดว่าคุณควรใช้ของ parallelGNU ฉันคิดว่ามันเป็นความคิดของคุณดำเนินการอย่างเต็มที่
motobói

5

ตัวอย่างอื่น:

ls *.txt | parallel 'sort {} > {.}.sorted.txt'

ฉันพบตัวอย่างอื่น ๆ ที่ซับซ้อนโดยไม่จำเป็นเมื่อส่วนใหญ่ข้างต้นเป็นสิ่งที่คุณอาจค้นหา


4

เครื่องมือที่มีอยู่ทั่วไปที่สามารถทำขนานได้ GNU make และอีกสองสามคนมี-jตัวเลือกในการสร้างบิลด์ขนาน

.SUFFIXES: .input .output
.input.output:
        process_one_file <$< >$@.tmp
        mv -f $@.tmp $@

ทำงานmakeแบบนี้ (ฉันสมมติว่าชื่อไฟล์ของคุณไม่มีอักขระพิเศษใด ๆ และmakeไม่ดีสำหรับมัน):

make -j 4 $(for x in *.input; do echo ${x%.*}.output; done)

IMHO นี้เป็นวิธีการแก้ปัญหาส่วนใหญ่ที่ฉลาด :)
h4unt3r

3

นี่คือการดำเนินการคำสั่งเดียวกันกับชุดของไฟล์ขนาดใหญ่ในไดเรกทอรีปัจจุบัน:

#!/bin/sh
trap 'worker=`expr $worker - 1`' USR1  # free up a worker
worker=0  # current worker
num_workers=10  # maximum number of workers
for file in *.txt; do
    if [ $worker -lt $num_workers ]; then
        {   customScript -c 33 -I -file $file -a -v 55 > `basename $file .txt`.outtxt 
            kill -USR1 $$ 2>/dev/null  # signal parent that we're free
        } &
        echo $worker/$num_worker $! $file  # feedback to caller
        worker=`expr $worker + 1`
    else
        wait # for a worker to finish
    fi
done

สิ่งนี้จะรันcustomScriptบนแต่ละtxtไฟล์วางเอาต์พุตในouttxtไฟล์ เปลี่ยนตามที่คุณต้องการ กุญแจสำคัญในการทำให้สิ่งนี้ทำงานได้คือการประมวลผลสัญญาณโดยใช้ SIGUSR1 เพื่อให้กระบวนการลูกสามารถบอกให้กระบวนการแม่รู้ว่ามันทำเสร็จแล้ว การใช้ SIGCHLD จะไม่ทำงานเนื่องจากคำสั่งส่วนใหญ่ในสคริปต์จะสร้างสัญญาณ SIGCHLD ให้กับเชลล์สคริปต์ ฉันพยายามแทนที่คำสั่งของคุณด้วยsleep 1, โปรแกรมใช้ 0.28s ของผู้ใช้ cpu และ 0.14s ของระบบ cpu; มีเพียง 400 ไฟล์เท่านั้น


'รอ' ฉลาดแค่ไหนที่จะใช้ไฟล์เดียวกันที่กำลังวนซ้ำแล้วป้อนคำสั่ง "if" ของพี่น้องอีกครั้ง?
J Jones

มันไม่ได้เป็นwaitที่ 'ฉลาด' พอ; แต่มันจะกลับมาหลังจากได้รับSIGUSR1สัญญาณ เด็ก / ผู้ปฏิบัติงานส่ง a SIGUSR1ไปยังผู้ปกครองซึ่งถูกจับ ( trap), และการลดลง$worker( trapข้อ) และกลับมาอย่างผิดปกติจากการwaitอนุญาตให้if [ $worker -lt $num_workers ]ข้อในการดำเนินการ
Arcege

0

หรือใช้เพียงxargs -Pไม่จำเป็นต้องติดตั้งซอฟต์แวร์เพิ่มเติม:

find . -type f -print0 | xargs -0 -I'XXX' -P4 -n1 custom_script -input "XXX" -output "XXX.out"

คำอธิบายเล็กน้อยสำหรับตัวเลือก:

  • -I'XXX' ชุดสตริงที่จะถูกแทนที่ในแม่แบบคำสั่งด้วยชื่อไฟล์
  • -P4 จะเรียกใช้ 4 กระบวนการแบบขนาน
  • -n1 จะใส่เพียงไฟล์เดียวต่อการดำเนินการแม้ว่าจะพบสอง XXX
  • -print0และ-0ทำงานร่วมกันเพื่อให้คุณมีอักขระพิเศษ (เช่นช่องว่าง) ในชื่อไฟล์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.