การดำเนินการคำสั่ง piped ในแบบคู่ขนาน


16

พิจารณาสถานการณ์สมมติต่อไปนี้ ฉันมีสองโปรแกรม A และ B. โปรแกรม A เอาต์พุตไปยัง stdout บรรทัดของสตริงในขณะที่โปรแกรม B ประมวลผลบรรทัดจาก stdin วิธีใช้โปรแกรมทั้งสองนี้เป็นของหลักสูตร:

foo @ bar: ~ $ A | B

ตอนนี้ฉันสังเกตเห็นว่านี่กินแกนเดียวเท่านั้น ดังนั้นฉันสงสัย:

โปรแกรม A และ B แชร์ทรัพยากรการคำนวณเดียวกันหรือไม่ ถ้าเป็นเช่นนั้นจะมีวิธีเรียกใช้ A และ B พร้อมกันไหม

อีกสิ่งหนึ่งที่ฉันสังเกตเห็นคือ A ทำงานเร็วกว่า B มากฉันจึงสงสัยว่าจะสามารถรันโปรแกรม B เพิ่มเติมได้หรือไม่และให้พวกเขาประมวลผลบรรทัดที่เอาท์พุทแบบขนาน

นั่นคือ A จะเอาต์พุตบรรทัดของมันและจะมีอินสแตนซ์ N ของโปรแกรม B ที่จะอ่านบรรทัดเหล่านี้ (ใครก็ตามที่อ่านก่อน) ประมวลผลและส่งออกใน stdout

ดังนั้นคำถามสุดท้ายของฉันคือ:

มีวิธีที่จะส่งออกท่อไปยัง A ในกระบวนการ B หลาย ๆ โดยไม่ต้องดูแลสภาพการแข่งขันและความไม่สอดคล้องอื่น ๆ ที่อาจเกิดขึ้น?


1
ในขณะที่A | B | Cขนานในกระบวนการที่แยกกันเนื่องจากลักษณะของท่อ (B ต้องรอเอาต์พุตของ A, C ต้องรอเอาต์พุตของ B) มันอาจยังคงเป็นเชิงเส้นในบางกรณี มันทั้งหมดขึ้นอยู่กับชนิดของผลผลิตที่พวกเขาผลิต มีหลายกรณีที่การเรียกใช้หลาย ๆ ครั้งBจะช่วยได้มากอาจเป็นไปได้ว่าตัวอย่าง wc แบบขนานช้ากว่าปกติwcเนื่องจากการแยกอาจใช้ทรัพยากรมากกว่าการนับบรรทัดตามปกติ ใช้ด้วยความระมัดระวัง
frostschutz

คำตอบ:


14

ปัญหาsplit --filterคือการที่สามารถผสมเอาท์พุทดังนั้นคุณจะได้รับครึ่งบรรทัดจากกระบวนการ 1 ตามด้วยครึ่งบรรทัดจากกระบวนการ 2

GNU Parallel รับประกันว่าจะไม่มีการมิกซ์

สมมติว่าคุณต้องการทำ:

 A | B | C

แต่ B นั้นช้ามากและคุณต้องการทำให้มันขนานกัน จากนั้นคุณสามารถทำได้:

A | parallel --pipe B | C

GNU Parallel โดยค่าเริ่มต้นจะแยกบน \ n และขนาดบล็อก 1 MB สามารถปรับได้ด้วย - รับและ - บล็อก

คุณสามารถหาข้อมูลเพิ่มเติมเกี่ยวกับ GNU Parallel ได้ที่: http://www.gnu.org/s/parallel/

คุณสามารถติดตั้ง GNU Parallel ในเวลาเพียง 10 วินาทีด้วย:

wget -O - pi.dk/3 | sh 

ดูวิดีโอแนะนำในhttp://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


1
ในขณะที่ฉันไม่เห็นด้วยอย่างยิ่งกับวิธีการติดตั้ง :-), +1 เนื่องจากโซลูชันของคุณแก้ปัญหาส่วนใหญ่ของฉันได้
LSerni

อันนี้ดีจริงๆ คุณมีข้อเสนอแนะสำหรับพารามิเตอร์ที่จะใช้หรือไม่? ฉันรู้ว่าโปรแกรม A จะส่งออกข้อมูลมากกว่า 1TB ประมาณ 5GB ต่อนาที โปรแกรม B ประมวลผลข้อมูลช้ากว่า A ถึง 5 เท่าและฉันมี 5 คอร์สำหรับการใช้งานนี้
Jernej

ขณะนี้ GNU Parallel สามารถจัดการได้ประมาณ 100 MB / s ดังนั้นคุณจะสัมผัสถึงขีด จำกัด นั้น อัตราส่วน--block-sizeจะขึ้นอยู่กับปริมาณของ RAM Bและวิธีการที่รวดเร็วคุณสามารถเริ่มต้นใหม่ ในสถานการณ์ของคุณฉันจะใช้--block 100Mและดูว่ามีประสิทธิภาพอย่างไร
Ole Tange

@lserni คุณสามารถหาวิธีการติดตั้งที่ดีกว่าซึ่งใช้กับเครื่อง UNIX ส่วนใหญ่และต้องการจำนวนงานที่คล้ายกันจากผู้ใช้หรือไม่
Ole Tange

4
ขอโทษฉันไม่ได้ทำให้ตัวเองชัดเจน วิธีการติดตั้ง - สคริปต์ส่งผ่านไปsh- ยอดเยี่ยม ปัญหาอยู่ในการผ่านมันไปดวลจุดโทษ: ดาวน์โหลดและเรียกรหัสปฏิบัติการจากเว็บไซต์ ในใจคุณบางทีฉันแค่หวาดระแวงเกินไปเพราะใคร ๆ ก็สามารถคัดค้านว่า RPM หรือ DEB ที่สร้างขึ้นเองนั้นเป็นสิ่งเดียวกันและแม้แต่การโพสต์โค้ดบนหน้าเว็บที่จะคัดลอกและวางจะส่งผลให้คนทำอย่างสุ่มสี่สุ่มห้า อย่างไรก็ตาม.
LSerni

13

เมื่อคุณเขียนA | Bกระบวนการทั้งสองแล้วทำงานแบบขนาน หากคุณเห็นว่าพวกเขาใช้เพียงแกนเดียวนั่นอาจเป็นเพราะการตั้งค่าความสัมพันธ์ของ CPU (อาจมีเครื่องมือบางอย่างในการวางกระบวนการที่มีความสัมพันธ์ต่างกัน) หรือเนื่องจากกระบวนการหนึ่งไม่เพียงพอที่จะเก็บทั้งแกนไว้และระบบ " ชอบ "ไม่ต้องใช้คอมพิวเตอร์

เพื่อให้ทำงานได้หลาย B กับหนึ่งที่คุณจะต้องใช้เครื่องมือเช่นsplitกับ--filterตัวเลือก:

A | split [OPTIONS] --filter="B"

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

ตัวเลือกอื่น ๆ ที่มีอยู่ (เช่นคุณสามารถ จำกัด ตัวอย่างของ B แต่ละคนเพื่อการส่งออกบรรทัดบัฟเฟอร์เดียวรอจนกว่าจะมีทั้ง "รอบ" ของ B ได้เสร็จสมบูรณ์เรียกใช้เทียบเท่าของลดการsplit's แผนที่และcatการส่งออกชั่วคราวร่วมกัน) ด้วยระดับประสิทธิภาพที่แตกต่างกัน ตัวเลือก 'รอบ' ที่อธิบายไว้เช่นจะรออินสแตนซ์ที่ช้าที่สุดของ Bให้เสร็จดังนั้นมันจะขึ้นอยู่กับการบัฟเฟอร์สำหรับ B [m]bufferอาจช่วยได้หรือไม่ก็ได้ขึ้นอยู่กับการปฏิบัติการ

ตัวอย่าง

สร้างตัวเลข 1,000 ตัวแรกและนับจำนวนคู่ขนาน:

seq 1 1000 | split -n r/10 -u --filter="wc -l"
100
100
100
100
100
100
100
100
100
100

หากเราต้อง "ทำเครื่องหมาย" บรรทัดเราจะเห็นว่าแต่ละบรรทัดแรกถูกส่งไปยังกระบวนการ # 1 แต่ละบรรทัดที่ห้าไปยังกระบวนการ # 5 และอื่น ๆ ยิ่งไปกว่านั้นในเวลาที่ใช้splitในการวางไข่กระบวนการที่สองวิธีแรกเป็นวิธีที่ดีในโควต้า:

seq 1 1000 | split -n r/10 -u --filter="sed -e 's/^/$RANDOM - /g'" | head -n 10
19190 - 1
19190 - 11
19190 - 21
19190 - 31
19190 - 41
19190 - 51
19190 - 61
19190 - 71
19190 - 81

เมื่อรันบนเครื่องแบบ 2 คอseqร์splitและwcกระบวนการแบ่งปันคอร์; แต่เมื่อมองใกล้ระบบจะออกจากสองกระบวนการแรกบน CPU0 และแบ่ง CPU1 ระหว่างกระบวนการของผู้ปฏิบัติงาน:

%Cpu0  : 47.2 us, 13.7 sy,  0.0 ni, 38.1 id,  1.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  : 15.8 us, 82.9 sy,  0.0 ni,  1.0 id,  0.0 wa,  0.3 hi,  0.0 si,  0.0 st
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM     TIME+ COMMAND
 5314 lserni    20   0  4516  568  476 R 23.9  0.0   0:03.30 seq
 5315 lserni    20   0  4580  720  608 R 52.5  0.0   0:07.32 split
 5317 lserni    20   0  4520  576  484 S 13.3  0.0   0:01.86 wc
 5318 lserni    20   0  4520  572  484 S 14.0  0.0   0:01.88 wc
 5319 lserni    20   0  4520  576  484 S 13.6  0.0   0:01.88 wc
 5320 lserni    20   0  4520  576  484 S 13.3  0.0   0:01.85 wc
 5321 lserni    20   0  4520  572  484 S 13.3  0.0   0:01.84 wc
 5322 lserni    20   0  4520  576  484 S 13.3  0.0   0:01.86 wc
 5323 lserni    20   0  4520  576  484 S 13.3  0.0   0:01.86 wc
 5324 lserni    20   0  4520  576  484 S 13.3  0.0   0:01.87 wc

สังเกตว่าโดยเฉพาะที่splitกินซีพียูเป็นจำนวนมาก สิ่งนี้จะลดลงตามความต้องการของ A; กล่าวคือถ้า A เป็นกระบวนการที่หนักกว่าseqค่าใช้จ่ายที่เกี่ยวข้องsplitจะลดลง แต่ถ้า A เป็นกระบวนการที่มีน้ำหนักเบามากและ B ค่อนข้างเร็ว (ดังนั้นคุณต้องการไม่เกิน 2-3 B เพื่อให้สอดคล้องกับ A) จากนั้นการขนานกับsplit(หรือท่อโดยทั่วไป) อาจไม่คุ้มค่า


น่าสนใจว่าตัวแยกที่พบใน Ubuntu ไม่มีตัวเลือก - ตัวกรอง OS ชนิดใดที่ใช้สำหรับสิ่งนี้
Jernej

Linux OpenSuSE 12.3 พร้อม coreutils ( gnu.org/software/coreutils/manual/html_node/… ) ฉันจะลองและถืออูบุนตูพวกเขาอาจเปลี่ยนชื่อเพื่อรองรับเครื่องมือที่มีชื่อคล้ายกัน
LSerni

คุณแน่ใจเกี่ยวกับsplit --filterตัวเลือกที่หายไป? ใน Ubuntu 12.04-LTS ("wheezy / sid") ของฉันมันอยู่ที่นั่นและตัวอย่างของฉันก็ใช้งานได้ คุณช่วยติดตั้งต่างsplitจาก coreutils ของ GNU ได้ไหม
LSerni

ขอบคุณสำหรับสิ่งนี้. ฉันต้องติดตั้ง Coreutils เวอร์ชันใหม่ BTW ฉันสังเกตว่าถ้าฉันรันโปรแกรม A เพียงอย่างเดียวมันกินคอร์ทั้งหมด (100%) ถ้าฉันรัน A | B จากนั้นพวกเขากินทั้งคอร์ประมวลผลการกิน 15% และการประมวลผลการกิน B 85% .. คุณเกิดขึ้นเพื่อดูว่าทำไมนี้เป็นเช่นนั้น?
Jernej

2
นี้น่าจะเป็นเพราะการปิดกั้น หาก B หนักกว่า A ดังนั้น A จะไม่สามารถส่งเอาต์พุตและชะลอความเร็วลงได้ ความเป็นไปได้อีกอย่างคือการยอมให้ B ระหว่างการทำงาน (เช่นดิสก์ / เน็ต) บนระบบอื่นคุณอาจเห็น B gobbling 100% ของ CPU1 และ A ได้รับการกำหนด 18% ของ CPU0 คุณอาจต้องการ 85/15 ~ 5.67 = ระหว่าง 5 ถึง 6 อินสแตนซ์ของ B เพื่อรับอินสแตนซ์ A เดี่ยวเพื่อทำให้แกนเดี่ยวอิ่มตัว I / O หากมีอยู่อาจทำให้ค่าเหล่านี้เบี่ยงเบนได้
LSerni
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.