เปลือกแบบขนาน


11

ฉันต้องการประมวลผลไฟล์จำนวนมากและเนื่องจากฉันมีแกนประมวลผลที่นี่ฉันต้องการทำแบบขนาน:

for i in *.myfiles; do do_something $i `derived_params $i` other_params; done

ฉันรู้จักโซลูชัน Makefile แต่คำสั่งของฉันต้องการอาร์กิวเมนต์จากรายการ globbing เชลล์ สิ่งที่ฉันพบคือ:

> function pwait() {
>     while [ $(jobs -p | wc -l) -ge $1 ]; do
>         sleep 1
>     done
> }
>

หากต้องการใช้งานสิ่งที่ต้องทำคือใส่ & หลังงานและการเรียก pwait พารามิเตอร์กำหนดจำนวนของกระบวนการแบบขนาน:

> for i in *; do
>     do_something $i &
>     pwait 10
> done

แต่มันใช้งานไม่ได้ดีมากเช่นฉันลองด้วยเช่นสำหรับการวนซ้ำการแปลงไฟล์จำนวนมาก แต่ให้ข้อผิดพลาดและเลิกงาน

ฉันไม่อยากจะเชื่อเลยว่านี่ยังไม่เสร็จเนื่องจากการสนทนาในรายชื่อผู้รับจดหมาย zsh นั้นเก่ามาก แล้วคุณจะรู้อะไรดีกว่านี้อีกไหม?


คล้ายกับคำถามนี้: superuser.com/questions/153630/…ดูว่าเทคนิคนั้นเหมาะกับคุณหรือไม่
JRobert

มันจะมีประโยชน์หากคุณโพสต์ข้อความแสดงข้อผิดพลาด
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

@JRobert ใช่ฉันรู้เรื่องนี้แล้ว แต่นี่ก็ไม่ได้ช่วยได้จริงเพราะวิธีการสร้างไฟล์จะไม่ทำงานอย่างที่ฉันพูด! @Dennis: ตกลงก่อนอื่นให้ฉันเรียกใช้ด้านบนแสดงให้ฉันเห็นมากกว่าจำนวนกระบวนการที่ระบุ ที่สองมันไม่ได้กลับไปที่พรอมต์อย่างถูกต้อง ข้อที่สามที่ฉันบอกว่าปล่อยให้งานเลิกทำไม่ถูกต้อง: ฉันเพิ่งวางตัวบ่งชี้echo "DONE"หลังจากวนรอบซึ่งถูกเรียกใช้ก่อนที่งานที่แอ็คทีฟจะไม่เสร็จ => นี่ทำให้ฉันคิดว่างานไม่ได้ทำ
คณิตศาสตร์

คำตอบ:


15

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

ข้อกำหนดสำหรับการลูปลิ่งไม่ใช่อุปสรรค: มีการนำไปใช้งานที่สนับสนุน GNU make ซึ่งมีการขยายสัญลักษณ์แทนเช่น$(wildcard *.c)และการเข้าถึงเปลือกเช่น$(shell mycommand)(ค้นหาฟังก์ชั่นใน GNU ทำคู่มือสำหรับข้อมูลเพิ่มเติม) เป็นค่าเริ่มต้นmakeบน Linux และมีอยู่ในระบบอื่นส่วนใหญ่ นี่คือโครงกระดูก Makefile ที่คุณสามารถปรับให้เข้ากับความต้องการของคุณ:

แหล่งข้อมูล = $ (wildcard * .src)

ทั้งหมด: $ (แหล่งที่มา: .src = .tgt)

% .tgt: $ .src
    do_something $ <$$ (ที่ได้มา _params $ <)> $ @

เรียกใช้บางสิ่งบางอย่างเช่นmake -j4เรียกใช้งานสี่งานพร้อมกันหรือmake -j -l3เพื่อให้ค่าเฉลี่ยการโหลดประมาณ 3


8

ฉันไม่แน่ใจว่าข้อโต้แย้งที่คุณได้รับนั้นเป็นอย่างไร แต่ด้วย GNU Parallel http: // www.gnu.org/software/parallel/ คุณสามารถทำได้เพื่อทำงานหนึ่งงานต่อซีพียูคอร์:

find . | parallel -j+0 'a={}; name=${a##*/}; upper=$(echo "$name" | tr "[:lower:]" "[:upper:]");
   echo "$name - $upper"'

หากสิ่งที่คุณต้องการได้รับคือการเปลี่ยน. extension {.} อาจมีประโยชน์:

parallel -j+0 lame {} -o {.}.mp3 ::: *.wav

ดูวิดีโอแนะนำสู่ GNU Parallel ได้ที่http://www.youtube.com/watch?v=OpaiGYxkSuQ


7

จะไม่ใช้waitคำสั่งของเชลล์ทำงานให้คุณ?

for i in *
do
    do_something $i &
done
wait

ลูปของคุณเรียกใช้งานจากนั้นรอมันจากนั้นจึงทำงานต่อไป ถ้าไปไม่ทำงานสำหรับคุณแล้วคุณอาจจะทำงานได้ดีขึ้นถ้าคุณย้ายหลังpwaitdone


ไม่เลยกับ 1 ล้านไฟล์ฉันจะมี 1 ล้านกระบวนการทำงานหรือฉันผิด
คณิตศาสตร์

1
@brubelsabs: ก็จะพยายามทำล้านกระบวนการ คุณไม่ได้พูดในคำถามของคุณว่าคุณต้องการประมวลผลไฟล์กี่ไฟล์ ฉันคิดว่าคุณจะต้องใช้forลูปซ้อนเพื่อ จำกัด ว่า: for file in *; do for i in {1..10}; do do_something "$i" & done; wait; done(ยังไม่ทดลอง) ที่ควรทำสิบครั้งและรอจนกว่าทั้งสิบกลุ่มจะเสร็จก่อนเริ่มสิบถัดไป วนรอบของคุณทำทีละครั้งในการสร้างที่&สงสัย ดูคำถามที่JRobertเชื่อมโยงกับตัวเลือกอื่น ๆ ค้นหา Stack Overflow สำหรับคำถามอื่น ๆ ที่คล้ายกับคำถามของคุณ (และคำถามอื่น)
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

หาก OP for i in *คาดล้านของไฟล์แล้วเขาจะมีปัญหากับ เขาจะต้องส่งผ่านข้อโต้แย้งไปยังลูปด้วยไพพ์หรือบางอย่าง จากนั้นแทนที่จะวนซ้ำภายในคุณสามารถเรียกใช้ตัวนับที่เพิ่มขึ้นและเรียกใช้"micro-"wait"-s"ทุก ๆ "$ ((i% 32))" -eq '0'

@DennisWilliamson: การรวมเข้าwaitกับตัวนับวงในทำงานได้ดีสำหรับฉัน ขอบคุณ!
Joel Purra

3

ทำไมไม่มีใครพูดถึง xargs เลย?

สมมติว่าคุณมีข้อโต้แย้งสามข้อ

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; done | xargs -n 3 -P $PROCS do_something

มิฉะนั้นให้ใช้ตัวคั่น (เป็นประโยชน์สำหรับสิ่งนั้น):

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; echo -ne "\0"; done | xargs -0 -n 1 -P $PROCS do_something

แก้ไข: สำหรับข้างต้นแต่ละพารามิเตอร์ควรคั่นด้วยอักขระ null แล้วจำนวนพารามิเตอร์ควรระบุด้วย xargs -n


ใช่ในโครงการของเรามีคนมีความคิดเดียวกันและใช้งานได้ดีแม้ใน Windows ด้วย MSys
คณิตศาสตร์

0

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

# Create a test file 
$ cat test.txt
task_test 1
task_test 2

# Create a shell source file 
$ cat task.sh
task_test()
{
    echo $1
}

# use the source under bash -c 
$ cat test.txt | xargs -n1 -I{} bash -c 'source task.sh; {}'
1
2

ดังนั้นสำหรับวิธีแก้ปัญหาของคุณจะเป็นอย่างไร

for i in *.myfiles; echo " do_something $i `derived_params $i` other_params
" >> commands.txt ; done

กำหนดทำบางสิ่งบางอย่างเป็น do_something.sh

do_something(){
process $1
echo $2 
whatever $3 

}

รันด้วยxargหรือgnu parallel

   cat commands.txt | xargs -n1 -I{} -P8 bash -c 'source do_something.sh; {}'

ฉันถือว่าความเป็นอิสระในการใช้งานของการวนซ้ำของสำหรับโดยนัย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.