Liệu ruby ​​có đa luồng thực sự?


295

Tôi biết về việc xâu chuỗi "hợp tác" của ruby ​​bằng các sợi màu xanh lá cây . Làm cách nào tôi có thể tạo các luồng "cấp hệ điều hành" thực trong ứng dụng của mình để sử dụng nhiều lõi cpu để xử lý?

Câu trả lời:


612

Cập nhật với bình luận tháng 9 năm 2011 của Jörg

Bạn dường như đang nhầm lẫn hai điều rất khác nhau ở đây: Ngôn ngữ lập trình Ruby và mô hình luồng cụ thể của một triển khai cụ thể của Ngôn ngữ lập trình Ruby. Hiện tại có khoảng 11 triển khai khác nhau của Ngôn ngữ lập trình Ruby, với rất các mô hình luồng khác nhau và độc đáo.

(Thật không may, chỉ có hai trong số những 11 triển khai thực sự sẵn sàng để sử dụng sản xuất, nhưng đến cuối năm con số có thể sẽ đi lên đến bốn hoặc năm.) ( Cập nhật : nó bây giờ 5: MRI, JRuby, YARV (người phiên dịch cho Ruby 1.9), Rubinius và IronRuby).

  1. Việc thực hiện đầu tiên không thực sự có tên, điều này khiến cho việc tham chiếu đến nó khá khó xử và thực sự gây phiền nhiễu và khó hiểu. Nó thường được gọi là "Ruby", thậm chí còn khó chịu và khó hiểu hơn là không có tên, vì nó dẫn đến sự nhầm lẫn vô tận giữa các tính năng của Ngôn ngữ lập trình Ruby và Thực hiện Ruby cụ thể.

    Đôi khi nó còn được gọi là "MRI" (cho "Matz's Ruby Thực hiện"), CRuby hoặc MatzRuby.

    MRI triển khai Chủ đề Ruby là Chủ đề xanh trong trình thông dịch . Thật không may, nó không cho phép các luồng đó được lên lịch song song, chúng chỉ có thể chạy một luồng mỗi lần.

    Tuy nhiên, bất kỳ số lượng Chủ đề C (Chủ đề POSIX, v.v.) đều có thể chạy song song với Chủ đề Ruby, vì vậy Thư viện C bên ngoài hoặc Phần mở rộng MRI C tạo các chủ đề của riêng chúng vẫn có thể chạy song song.

  2. Việc triển khai thứ hai là YARV (viết tắt của "Yet Another Ruby VM"). YARV triển khai Chủ đề Ruby dưới dạng Chủ đề POSIX hoặc Windows NT , tuy nhiên, nó sử dụng Khóa phiên dịch toàn cầu (GIL) để đảm bảo rằng chỉ có một Chủ đề Ruby thực sự có thể được lên lịch bất kỳ lúc nào.

    Giống như MRI, Chủ đề C thực sự có thể chạy song song với Chủ đề Ruby.

    Trong tương lai, có thể, GIL có thể được chia thành các khóa hạt mịn hơn, do đó cho phép ngày càng nhiều mã thực sự chạy song song, nhưng điều đó còn rất xa, nó thậm chí còn chưa được lên kế hoạch .

  3. JRuby thực hiện Chủ đề Ruby là Chủ đề gốc , trong đó "Chủ đề gốc" trong trường hợp JVM rõ ràng có nghĩa là "Chủ đề JVM". JRuby áp đặt không có khóa bổ sung trên chúng. Vì vậy, việc các luồng đó có thực sự chạy song song hay không phụ thuộc vào JVM: một số JVM triển khai Chủ đề JVM làm Chủ đề hệ điều hành và một số là Chủ đề xanh. (Các JVM chính từ Sun / Oracle sử dụng các luồng hệ điều hành độc quyền kể từ JDK 1.3)

  4. XRuby cũng thực hiện Chủ đề Ruby là Chủ đề JVM . Cập nhật : XRuby đã chết.

  5. IronRuby thực hiện Chủ đề Ruby là Chủ đề gốc , trong đó "Chủ đề gốc" trong trường hợp CLR rõ ràng có nghĩa là "Chủ đề CLR". IronRuby không áp dụng khóa bổ sung cho họ, vì vậy, họ nên chạy song song, miễn là CLR của bạn hỗ trợ điều đó.

  6. Ruby.NET cũng thực hiện Chủ đề Ruby là Chủ đề CLR . Cập nhật: Ruby.NET đã chết.

  7. Rubinius thực hiện Chủ đề Ruby là Chủ đề xanh trong Máy ảo của mình . Chính xác hơn: VM Rubinius xuất một cấu trúc dòng chảy đồng thời / song song / điều khiển không cục bộ rất nhẹ, được gọi là " Nhiệm vụ " và tất cả các cấu trúc đồng thời khác (Chủ đề trong cuộc thảo luận này, cũng như Tiếp tục , Diễn viên và các công cụ khác ) được triển khai trong Ruby thuần túy, sử dụng Nhiệm vụ.

    Tuy nhiên, Rubinius không thể lập lịch trình các Chủ đề song song, tuy nhiên, việc thêm đó không phải là vấn đề quá lớn: Rubinius có thể chạy một số phiên bản VM trong một số Chủ đề POSIX song song , trong một quy trình Rubinius. Do Chủ đề thực sự được triển khai trong Ruby, nên chúng có thể, giống như bất kỳ đối tượng Ruby nào khác, được tuần tự hóa và gửi đến một VM khác trong một Chủ đề POSIX khác. (Đó là mô hình tương tự BEAM Erlang VM sử dụng cho đồng thời SMP. Nó đã được triển khai cho Rubinius Actors .)

    Cập nhật : Thông tin về Rubinius trong câu trả lời này là về Shotgun VM, không còn tồn tại nữa. Máy ảo C ++ "mới" không sử dụng các luồng màu xanh lá cây được lên lịch trên nhiều máy ảo (tức là kiểu Erlang / BEAM), nó sử dụng một máy ảo đơn truyền thống hơn với nhiều mô hình luồng hệ điều hành gốc, giống như mô hình được sử dụng bởi, như CLR, Mono và khá nhiều JVM.

  8. MacRuby khởi đầu là một cổng của YARV trên đỉnh của Khung công tác Objective-C Runtime và CoreFoundation và Ca cao. Hiện tại nó đã được chuyển hướng đáng kể khỏi YARV, nhưng AFAIK hiện tại vẫn chia sẻ cùng một Mô hình luồng với YARV . Cập nhật: MacRuby phụ thuộc vào trình thu gom rác táo được tuyên bố không dùng nữa và sẽ bị xóa trong các phiên bản sau của MacOSX, MacRuby là undead.

  9. Hồng y là một triển khai Ruby cho máy ảo Parrot . Nó chưa thực hiện các chủ đề, tuy nhiên, khi có, nó có thể sẽ triển khai chúng dưới dạng Chủ đề Parrot . Cập nhật : Hồng y dường như rất không hoạt động / đã chết.

  10. MagLev là một triển khai Ruby cho máy ảo Gemtone / S Smalltalk . Tôi không có thông tin về mô hình luồng mà GemStone / S sử dụng, mô hình luồng nào MagLev sử dụng hoặc ngay cả khi các luồng thậm chí còn được triển khai (có thể không).

  11. HotRubykhông một Ruby Thực hiện đầy đủ của riêng nó. Đây là một triển khai của VM mã byte YARV trong JavaScript. HotRuby không hỗ trợ các luồng (chưa?) Và khi có, chúng sẽ không thể chạy song song, vì JavaScript không hỗ trợ cho tính song song thực sự. Tuy nhiên, có một phiên bản ActionScript của HotRuby và ActionScript thực sự có thể hỗ trợ song song. Cập nhật : HotRuby đã chết.

Thật không may, chỉ có hai trong số 11 triển khai Ruby này thực sự sẵn sàng sản xuất: MRI và JRuby.

Vì vậy, nếu bạn muốn các luồng song song thực sự, JRuby hiện là lựa chọn duy nhất của bạn - không phải là một điều xấu: JRuby thực sự nhanh hơn MRI và ổn định hơn.

Mặt khác, giải pháp Ruby "cổ điển" là sử dụng các quy trình thay vì các luồng để xử lý song song. Thư viện Ruby Core chứa Processmô-đun với Process.fork phương thức giúp dễ dàng tách ra một quy trình Ruby khác. Ngoài ra, Thư viện Ruby Standard chứa thư viện Phân phối Ruby (dRuby / dRb) , cho phép mã Ruby được phân phối tầm thường trên nhiều quy trình, không chỉ trên cùng một máy mà còn trên toàn mạng.


1
nhưng sử dụng ngã ba sẽ phá vỡ việc sử dụng trên jruby ... chỉ cần nói
akostadinov

1
Đây là một câu trả lời tuyệt vời. Tuy nhiên, nó là đối tượng của rất nhiều liên kết thối. Tôi không biết những tài nguyên này có thể đã di chuyển ở đâu.
BlackV ăn được

28

Ruby 1.8 chỉ có các luồng màu xanh lá cây, không có cách nào để tạo ra một luồng "cấp độ hệ điều hành" thực sự. Nhưng, ruby ​​1.9 sẽ có một tính năng mới gọi là sợi, cho phép bạn tạo các luồng cấp độ hệ điều hành thực tế. Thật không may, Ruby 1.9 vẫn đang trong giai đoạn thử nghiệm, nó được lên kế hoạch ổn định trong một vài tháng.

Một cách khác là sử dụng JRuby. JRuby thực hiện các luồng như các đầu mối cấp hệ điều hành, không có "luồng xanh" nào trong đó. Phiên bản mới nhất của JRuby là 1.1.4 và tương đương với Ruby 1.8


35
Thật sai lầm khi Ruby 1.8 chỉ có các luồng màu xanh lá cây, một số triển khai của Ruby 1.8 có các luồng gốc: JRuby, XRuby, Ruby.NET và IronRuby. Sợi không cho phép tạo ra các luồng gốc, chúng nhẹ hơn các luồng. Họ thực sự là bán coroutines, tức là họ hợp tác.
Jörg W Mittag

19
Tôi nghĩ khá rõ ràng từ câu trả lời của Josh rằng anh ta có nghĩa là Ruby 1.8 thời gian chạy, còn gọi là MRI, chứ không phải ngôn ngữ của Ruby 1.8, khi anh ta nói Ruby 1.8.
Theo

@Theo Cũng rõ ràng là anh ấy làm rối tung các khái niệm trong câu trả lời của mình. Các sợi không phải là một cách để tạo ra các luồng gốc, như đã đề cập, chúng thậm chí còn nhẹ hơn các luồng và hiện tại có các luồng gốc nhưng với GIL.
Sở thú Foo Bar

8

Nó phụ thuộc vào việc thực hiện:

  • MRI không có, YARV gần hơn.
  • JRuby và MacRuby có.




Ruby có đóng cửa như Blocks, lambdasProcs. Để tận dụng tối đa lợi thế của việc đóng và nhiều lõi trong JRuby, các nhà điều hành của Java có ích; đối với MacRuby, tôi thích hàng đợi của GCD .

Lưu ý rằng, có thể tạo các luồng "cấp độ hệ điều hành" thực sự không có nghĩa là bạn có thể sử dụng nhiều lõi cpu để xử lý song song. Nhìn vào các ví dụ dưới đây.

Đây là đầu ra của một chương trình Ruby đơn giản sử dụng 3 luồng sử dụng Ruby 2.1.0:

(jalcazar@mac ~)$ ps -M 69877
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 69877 s002    0.0 S    31T   0:00.01   0:00.04 /Users/jalcazar/.rvm/rubies/ruby-2.1.0/bin/ruby threads.rb
   69877         0.0 S    31T   0:00.01   0:00.00 
   69877        33.4 S    31T   0:00.01   0:08.73 
   69877        43.1 S    31T   0:00.01   0:08.73 
   69877        22.8 R    31T   0:00.01   0:08.65 

Như bạn có thể thấy ở đây, có bốn luồng hệ điều hành, tuy nhiên chỉ có một luồng có trạng thái Rđang chạy. Điều này là do một hạn chế trong cách thực hiện các chủ đề của Ruby.



Cùng một chương trình, bây giờ với JRuby. Bạn có thể thấy ba luồng với trạng thái R, có nghĩa là chúng đang chạy song song.

(jalcazar@mac ~)$ ps -M 72286
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 72286 s002    0.0 S    31T   0:00.01   0:00.01 /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/bin/java -Djdk.home= -Djruby.home=/Users/jalcazar/.rvm/rubies/jruby-1.7.10 -Djruby.script=jruby -Djruby.shell=/bin/sh -Djffi.boot.library.path=/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni/Darwin -Xss2048k -Dsun.java.command=org.jruby.Main -cp  -Xbootclasspath/a:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jruby.jar -Xmx1924M -XX:PermSize=992m -Dfile.encoding=UTF-8 org/jruby/Main threads.rb
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    33T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.09   0:02.34 
   72286         7.9 S    31T   0:00.15   0:04.63 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.04   0:01.68 
   72286         0.0 S    31T   0:00.03   0:01.54 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.01   0:00.01 
   72286         0.0 S    31T   0:00.00   0:00.01 
   72286         0.0 S    31T   0:00.00   0:00.03 
   72286        74.2 R    31T   0:09.21   0:37.73 
   72286        72.4 R    31T   0:09.24   0:37.71 
   72286        74.7 R    31T   0:09.24   0:37.80 


Chương trình tương tự, bây giờ với MacRuby. Ngoài ra còn có ba luồng chạy song song. Điều này là do các luồng MacRuby là các luồng POSIX ( các luồng "cấp độ hệ điều hành" thực sự ) và không có GVL

(jalcazar@mac ~)$ ps -M 38293
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 38293 s002    0.0 R     0T   0:00.02   0:00.10 /Users/jalcazar/.rvm/rubies/macruby-0.12/usr/bin/macruby threads.rb
   38293         0.0 S    33T   0:00.00   0:00.00 
   38293       100.0 R    31T   0:00.04   0:21.92 
   38293       100.0 R    31T   0:00.04   0:21.95 
   38293       100.0 R    31T   0:00.04   0:21.99 


Một lần nữa, cùng một chương trình nhưng bây giờ với MRI cũ tốt. Do thực tế là việc triển khai này sử dụng các luồng màu xanh lá cây, chỉ có một luồng hiển thị

(jalcazar@mac ~)$ ps -M 70032
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 70032 s002  100.0 R    31T   0:00.08   0:26.62 /Users/jalcazar/.rvm/rubies/ruby-1.8.7-p374/bin/ruby threads.rb



Nếu bạn quan tâm đến đa luồng Ruby, bạn có thể thấy báo cáo của tôi Chương trình gỡ lỗi song song bằng cách sử dụng trình xử lý ngã ba thú vị.
Để có cái nhìn tổng quan hơn về các phần bên trong của Ruby, Ruby Under a Kính hiển vi là một bài đọc tốt.
Cũng thế, Ruby Themes và Khóa phiên dịch toàn cầu bằng C trong Omniref giải thích trong mã nguồn tại sao các chuỗi Ruby không chạy song song.


Theo RMI, ý bạn là MRI?
Mayuresh Srivastava

4

Làm thế nào về việc sử dụng drb ? Nó không thực sự đa luồng nhưng giao tiếp giữa một số quy trình, nhưng bạn có thể sử dụng nó ngay bây giờ trong 1.8 và nó có độ ma sát khá thấp.


3

Tôi sẽ để "Giám sát hệ thống" trả lời câu hỏi này. Tôi đang thực thi cùng một mã (bên dưới, tính toán các số nguyên tố) với 8 luồng Ruby chạy trên máy i7 (4 siêu phân luồng) trong cả hai trường hợp ... lần chạy đầu tiên là:

jruby 1.5.6 (ruby 1.8.7 patchlevel 249) (2014/02/03 6586) (Máy chủ OpenJDK 64-bit 1.7.0_75) [amd64-java]

Thứ hai là với:

ruby 2.1.2p95 (2014-05-08) [x86_64-linux-gnu]

Điều thú vị là CPU cao hơn đối với các luồng của JRuby, nhưng thời gian hoàn thành ngắn hơn một chút đối với Ruby được giải thích. Thật khó để nói từ biểu đồ, nhưng lần chạy thứ hai (được giải thích là Ruby) sử dụng khoảng 1/2 CPU (không có siêu phân luồng?)

nhập mô tả hình ảnh ở đây

def eratosthenes(n)
  nums = [nil, nil, *2..n]
  (2..Math.sqrt(n)).each do |i|
    (i**2..n).step(i){|m| nums[m] = nil}  if nums[i]
  end
  nums.compact
end

MAX_PRIME=10000000
THREADS=8
threads = []

1.upto(THREADS) do |num|
  puts "Starting thread #{num}"
  threads[num]=Thread.new { eratosthenes MAX_PRIME }
end

1.upto(THREADS) do |num|
    threads[num].join
end

1

Nếu bạn đang sử dụng MRI, thì bạn có thể viết mã luồng trong C dưới dạng phần mở rộng hoặc sử dụng đá quý ruby-inline.


1

Nếu bạn thực sự cần sự song song trong Ruby cho một hệ thống cấp độ Sản xuất (nơi bạn không thể sử dụng bản beta) thì có thể là một sự thay thế tốt hơn.
Nhưng, nó chắc chắn là đáng thử các chủ đề dưới JRuby đầu tiên.

Ngoài ra nếu bạn quan tâm đến tương lai của luồng trong Ruby, bạn có thể thấy bài viết này hữu ích.


JRuby là một lựa chọn tốt. Để xử lý song song bằng cách sử dụng các quy trình, tôi thích github.com/grosser/abul Parallel.map(['a','b','c'], :in_processes=>3){...
user454322


1

Bởi vì không thể chỉnh sửa câu trả lời đó, vì vậy hãy thêm câu trả lời mới vào đây.

Cập nhật (2017-05-08)

Bài viết này rất cũ và thông tin không theo dõi bước đi hiện tại (2017), Sau đây là một số bổ sung:

  1. Opal là trình biên dịch mã nguồn-nguồn từ Ruby sang JavaScript. Nó cũng có một triển khai của Ruby corelib, Nó hiện đang phát triển rất tích cực và tồn tại rất nhiều khung (frontend) hoạt động trên nó. và sản xuất đã sẵn sàng. Bởi vì dựa trên javascript, nó không hỗ trợ các luồng song song.

  2. truffleruby là một triển khai hiệu năng cao của ngôn ngữ lập trình Ruby. Được xây dựng trên GraalVM bởi Oracle Labs, TruffleRuby là một nhánh của JRuby, kết hợp nó với mã từ dự án Rubinius và cũng chứa mã từ triển khai tiêu chuẩn của Ruby, MRI, vẫn phát triển trực tiếp, chưa sẵn sàng sản xuất. Phiên bản ruby ​​này có vẻ như được sinh ra để thực hiện, tôi không biết nếu hỗ trợ các luồng song song, nhưng tôi nghĩ nó nên.

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.