Thực hiện các lệnh đường ống song song


16

Hãy xem xét các kịch bản sau đây. Tôi có hai chương trình A và B. Chương trình A xuất ra các dòng stdout trong khi chương trình B xử lý các dòng từ stdin. Cách sử dụng hai chương trình này là tất nhiên:

thanh foo @: ~ $ A | B

Bây giờ tôi đã nhận thấy rằng điều này chỉ ăn một lõi; do đó tôi tự hỏi:

Các chương trình A và B có chia sẻ cùng một tài nguyên tính toán không? Nếu vậy, có cách nào để chạy A và B đồng thời không?

Một điều khác mà tôi nhận thấy là A chạy nhanh hơn nhiều so với B, do đó tôi tự hỏi liệu bằng cách nào đó có thể chạy nhiều chương trình B hơn và để chúng xử lý các dòng mà A xuất ra song song.

Nghĩa là, A sẽ xuất các dòng của nó và sẽ có N phiên bản của các chương trình B sẽ đọc các dòng này (bất cứ ai đọc chúng trước) sẽ xử lý chúng và xuất chúng trên thiết bị xuất chuẩn.

Vì vậy, câu hỏi cuối cùng của tôi là:

Có cách nào để dẫn đầu ra đến A trong số một số quy trình B mà không phải quan tâm đến các điều kiện chủng tộc và những mâu thuẫn khác có khả năng phát sinh không?


1
Mặc dù A | B | Csong song như trong các quy trình riêng biệt, do tính chất của đường ống (B phải chờ đầu ra của A, C phải chờ đầu ra của B), nó vẫn có thể là tuyến tính trong một số trường hợp. Nó hoàn toàn phụ thuộc vào loại sản phẩm họ sản xuất. Không có nhiều trường hợp chạy nhiều Bsẽ giúp ích nhiều, hoàn toàn có thể ví dụ wc song song chậm hơn bình thường wcvì việc chia tách có thể chiếm nhiều tài nguyên hơn so với đếm các dòng thông thường. Sử dụng cẩn thận.
frostschutz

Câu trả lời:


14

Một vấn đề với split --filterlà đầu ra có thể bị lẫn lộn, do đó bạn nhận được một nửa dòng từ quy trình 1 và một nửa dòng từ quy trình 2.

GNU Parallel đảm bảo sẽ không có sự pha trộn.

Vì vậy, giả sử bạn muốn làm:

 A | B | C

Nhưng B rất chậm, và do đó bạn muốn song song hóa điều đó. Sau đó, bạn có thể làm:

A | parallel --pipe B | C

GNU Parallel theo mặc định phân tách trên \ n và kích thước khối là 1 MB. Điều này có thể được điều chỉnh với --recend và --block.

Bạn có thể tìm hiểu thêm về GNU Parallel tại: http://www.gnu.org/s/abul/

Bạn có thể cài đặt GNU Parallel chỉ trong 10 giây với:

wget -O - pi.dk/3 | sh 

Xem video giới thiệu trên http://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


1
Mặc dù tôi hoàn toàn không đồng ý với phương pháp cài đặt :-), +1 vì giải pháp của bạn giải quyết được hầu hết các vấn đề với tôi.
LSerni

Điều này là thực sự tốt đẹp. Bạn cũng có bất kỳ đề xuất cho các tham số sẽ được sử dụng? Tôi biết chương trình A sẽ xuất ra hơn 1TB dữ liệu khoảng 5 GB mỗi phút. Chương trình B xử lý dữ liệu chậm hơn 5 lần so với A xuất ra và tôi có 5 lõi theo ý của mình cho nhiệm vụ này.
Jernej

GNU Parallel hiện tại có thể xử lý tối đa khoảng 100 MB / s, vì vậy bạn sẽ chạm vào giới hạn đó. Tối ưu --block-sizesẽ phụ thuộc vào dung lượng RAM và tốc độ bạn có thể bắt đầu mới B. Trong tình huống của bạn, tôi sẽ sử dụng --block 100Mvà xem cách thực hiện.
Ole Tange

@lserni Bạn có thể đưa ra một phương pháp cài đặt tốt hơn, hoạt động trên hầu hết các máy UNIX và yêu cầu số lượng công việc tương tự từ người dùng không?
Ole Tange

4
Xin lỗi, tôi đã không làm cho mình rõ ràng. Phương pháp cài đặt - tập lệnh được truyền tới sh- là tuyệt vời. Vấn đề nằm ở việc chuyển nó sang sh: tải xuống và chạy mã thực thi từ một trang web . Nhắc bạn, có lẽ tôi chỉ là quá hoang tưởng, vì người ta có thể phản đối rằng RPM hoặc DEB tùy chỉnh về cơ bản là giống nhau, và thậm chí đăng mã lên một trang để sao chép và dán sẽ dẫn đến việc mọi người làm điều đó một cách mù quáng dù sao.
LSerni

13

Khi bạn viết A | B, cả hai quá trình đã chạy song song. Nếu bạn thấy chúng chỉ sử dụng một lõi, thì đó có thể là do cài đặt mối quan hệ CPU (có lẽ có một số công cụ để tạo ra một quy trình có ái lực khác nhau) hoặc vì một quy trình không đủ để giữ toàn bộ lõi và hệ thống " thích "không phổ biến điện toán.

Để chạy một số B với một A, bạn cần một công cụ như splitvới --filtertùy chọn:

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

Tuy nhiên, điều này có khả năng làm xáo trộn thứ tự các dòng trong đầu ra, bởi vì các công việc B sẽ không chạy cùng tốc độ. Nếu đây là một vấn đề, bạn có thể cần phải chuyển hướng đầu ra B i-th sang một tệp trung gian và gắn chúng lại với nhau ở cuối bằng cách sử dụng cat. Điều này, đến lượt nó, có thể yêu cầu một không gian đĩa đáng kể.

Các tùy chọn khác tồn tại (ví dụ bạn có thể giới hạn mỗi thể hiện của B để một đầu ra dòng đệm đơn, chờ đợi cho đến khi toàn bộ "vòng" của B đã hoàn tất, chạy tương đương với một giảm để split's bản đồ , và catđầu ra tạm thời với nhau), với mức độ hiệu quả khác nhau. Tùy chọn 'round' vừa được mô tả chẳng hạn sẽ đợi trường hợp B chậm nhất kết thúc, do đó, nó sẽ phụ thuộc rất nhiều vào bộ đệm có sẵn cho B; [m]buffercó thể giúp hoặc có thể không, tùy thuộc vào hoạt động là gì.

Ví dụ

Tạo 1000 số đầu tiên và đếm các dòng song song:

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

Nếu chúng ta "đánh dấu" các dòng, chúng ta sẽ thấy rằng mỗi dòng đầu tiên được gửi đến quy trình số 1, mỗi dòng thứ năm để xử lý số 5, v.v. Hơn nữa, trong thời gian cần thiết splitđể sinh ra quá trình thứ hai, lần đầu tiên đã là một cách tốt để đạt được hạn ngạch của nó:

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

Khi thực hiện trên một máy 2 lõi, seq, splitwcquá trình chia sẻ các lõi; nhưng nhìn kỹ hơn, hệ thống để lại hai tiến trình đầu tiên trên CPU0 và chia CPU1 cho các tiến trình worker:

%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

Đặc biệt lưu ý rằng đó splitlà ăn một lượng đáng kể CPU. Điều này sẽ giảm tỷ lệ thuận với nhu cầu của A; tức là, nếu A là một quá trình nặng hơn seq, thì chi phí tương đối splitsẽ giảm. Nhưng nếu A là một quá trình rất nhẹ và B khá nhanh (do đó bạn không cần nhiều hơn 2-3 B để theo kịp A), thì song song với split(hoặc các đường ống nói chung) có thể không có giá trị.


Điều thú vị là phần tách được tìm thấy trên Ubuntu không có tùy chọn --filter. Những loại hệ điều hành đang sử dụng cho việc này?
Jernej

Linux OpenSuSE 12.3, với coreutils ( gnu.org/software/coreutils/manual/html_node/ trộm ). Tôi sẽ thử và nắm giữ một Ubuntu, họ có thể đã thay đổi tên để phù hợp với một số công cụ có tên tương tự.
LSerni

Bạn có chắc chắn về split --filtertùy chọn bị thiếu? Trên Ubuntu 12.04-LTS của tôi ("wheezy / sid"), nó ở đó và các ví dụ của tôi hoạt động. Bạn có thể đã cài đặt một cái khác splitvới cái trong lõi core GNU không?
LSerni

Cảm ơn vì điều đó. Tôi đã phải cài đặt một phiên bản mới hơn của Coreutils. BTW, tôi đã nhận thấy rằng nếu tôi chạy chương trình A một mình thì nó ăn toàn bộ lõi (100%) nếu tôi chạy A | B sau đó họ cùng nhau ăn toàn bộ lõi, quá trình A ăn 15% và quá trình B ăn 85% .. Bạn có tình cờ thấy tại sao lại như vậy không?
Jernej

2
Điều này có thể là do chặn . Nếu B nặng hơn A, thì A không thể gửi đầu ra của nó và bị chậm lại. Một khả năng khác là A mang lại cho B trong quá trình hoạt động (ví dụ: đĩa / mạng). Trên một hệ thống khác, bạn có thể thấy B đang ngấu nghiến 100% CPU1 và A được chỉ định 18% CPU0. Bạn có thể cần 85/15 ~ 5.67 = từ 5 đến 6 phiên bản B để có được một cá thể A duy nhất để bão hòa một lõi đơn. I / O, nếu có, có thể làm lệch các giá trị này, mặc dù.
LSerni
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.