Чи має рубін справжню багатопоточність?


295

Я знаю про "кооперативну" нарізку рубіну із використанням зелених ниток . Як я можу створити справжні потоки на рівні ОС у своєму додатку, щоб використовувати декілька ядер процесора для обробки?

Відповіді:


612

Оновлено коментарем Йорга з вересня 2011 року

Ви, здається, тут плутаєте дві дуже різні речі: мову програмування Ruby та специфічну модель нитки однієї конкретної реалізації мови програмування Ruby. В даний час існує близько 11 різних реалізацій мови програмування Ruby, з дуже різними і унікальними моделями різьблення.

(На жаль, лише два з цих 11 реалізацій фактично готові до використання у виробництві, але до кінця року ця кількість, ймовірно, збільшиться до чотирьох-п'яти.) ( Оновлення : зараз це 5: MRI, JRuby, YARV (інтерпретатор) для Ruby 1.9), Rubinius та IronRuby).

  1. Перша реалізація насправді не має назви, що робить її досить незручною для посилання на неї та насправді дратує та заплутує. Найчастіше його називають "Ruby", що ще більше дратує і плутає, ніж відсутність імені, тому що це призводить до нескінченного плутанини між особливостями мови програмування Ruby та певною реалізацією Ruby.

    Його також іноді називають "МРТ" (для "реалізації Matz's Ruby"), CRuby або MatzRuby.

    MRI реалізує Ruby Threads як «Зелені нитки» у своєму інтерпретаторі . На жаль, це не дозволяє паралельно планувати ці потоки, вони можуть запускати лише один потік.

    Однак будь-яка кількість ниток C (POSIX Threads тощо) може працювати паралельно потоці Ruby, тому зовнішні бібліотеки C або розширення MRI C, які створюють власні нитки, все ще можуть працювати паралельно.

  2. Друга реалізація - YARV (скорочення "Ще один Ruby VM"). YARV реалізує Ruby Threads як POSIX або Windows NT Threads , однак він використовує глобальне блокування інтерпретаторів (GIL), щоб гарантувати, що лише одна Ruby Thread може бути фактично запланована в будь-який час.

    Як і МРТ, нитки C можуть насправді працювати паралельно Ruby Threads.

    В майбутньому можливо, що GIL може розбитись на більш дрібнозернисті замки, тим самим дозволяючи все більше коду фактично працювати паралельно, але це поки що далеко, це ще не планується .

  3. JRuby реалізує Ruby Threads як Native Threads , де "Native Threads" у випадку JVM очевидно означає "JVM Threads". JRuby не накладає на них додаткових блокувань. Отже, чи справді ці потоки можуть працювати паралельно, залежить від JVM: деякі JVM реалізують потоки JVM як потоки ОС, а деякі - як зелені нитки. (Основні JVM від Sun / Oracle використовують виключно потоки ОС з JDK 1.3)

  4. XRuby також реалізує Ruby Threads як JVM Threads . Оновлення : XRuby мертвий.

  5. IronRuby реалізує Ruby Threads як Native Threads , де "Native Threads" у випадку CLR очевидно означає "CLR Threads". IronRuby не накладає на них додаткового блокування, тому вони повинні працювати паралельно, доки ваш CLR це підтримує.

  6. Ruby.NET також реалізує Ruby Threads як CLR Threads . Оновлення: Ruby.NET мертвий.

  7. Rubinius реалізує Ruby Threads як зелені нитки у своїй віртуальній машині . Точніше: Рубіній ВМ експортує дуже легку, дуже гнучку конструкцію паралелізму / паралелізму / не локальних контрольних потоків, яку називають " Завданням ", та всі інші конструкції одночасності (теми в цій дискусії, але також продовження , актори та ін. ) реалізуються в чистому Ruby, використовуючи Завдання.

    Рубіній не може (наразі) планувати потоки паралельно, однак додавши, що це не так вже й багато проблем: Рубіній вже може запускати кілька екземплярів VM у декількох потоках POSIX паралельно , протягом одного процесу Рубінія. Оскільки потоки реально реалізовані в Ruby, вони можуть, як і будь-який інший об'єкт Ruby, бути серіалізовані та відправлені до іншого VM в іншій POSIX потоці. (Це та сама модель, яку використовує BEAM Erlang VM для одночасності SMP. Вона вже реалізована для Rubinius Actors .)

    Оновлення : Інформація про Рубінія в цій відповіді стосується Shotgun VM, якого вже не існує. "Новий" C ++ VM не використовує зелені потоки, заплановані в декількох віртуальних машинах (тобто стиль Erlang / BEAM), він використовує більш традиційну одиночну машину управління з декількома натільними моделями потоків ОС, подібно до тієї, яку використовують, скажімо, CLR, Mono , і майже кожен JVM.

  8. MacRuby стартував як порт YARV, що знаходиться на вершині рамки виконання Objective-C та CoreFoundation та какао. Зараз він суттєво відхилився від YARV, але в даний час AFAIK все ще поділяє ту ж модель Threading з YARV . Оновлення: MacRuby залежить від збору сміття з яблук, який оголошено застарілим і буде видалено в пізніших версіях MacOSX, MacRuby нежить.

  9. Кардинал - реалізація Ruby для віртуальної машини Parrot . Він ще не реалізує потоки, однак, коли це станеться, він, ймовірно, реалізує їх у вигляді папужок . Оновлення : Кардинал здається дуже неактивним / мертвим.

  10. MagLev - реалізація Ruby для VM GemStone / S Smalltalk . У мене немає інформації, що використовує модель для нарізки GemStone / S, яку модель нарізки використовує MagLev або навіть якщо потоки ще реалізовані (напевно, ні).

  11. HotRuby це НЕ повний Рубін Здійснення своєї власної. Це реалізація байт-коду YARV байтового коду в JavaScript. HotRuby не підтримує потоки (ще?), І коли це станеться, вони не зможуть запускатися паралельно, оскільки JavaScript не підтримує справжній паралелізм. Однак існує версія ActionScript HotRuby, але ActionScript насправді може підтримувати паралелізм. Оновлення : HotRuby мертвий.

На жаль, лише два з цих 11 впроваджень Ruby вже готові до виробництва: MRI та JRuby.

Отже, якщо ви хочете справжніх паралельних потоків, на даний момент JRuby - ваш єдиний вибір - не те, що це погано: JRuby насправді швидший, ніж МРТ, і, можливо, більш стабільний.

В іншому випадку "класичним" рішенням Рубі є використання процесів замість ниток для паралелізму. Основна бібліотека Ruby містить Processмодуль із Process.fork методом, який дозволяє легко відключити інший процес Ruby. Також стандартна бібліотека Ruby містить бібліотеку розподіленого Ruby (dRuby / dRb) , яка дозволяє тривимірно розподіляти код Ruby по декількох процесах не тільки на одній машині, але і по всій мережі.


1
але використання fork порушить використання jruby ... просто кажу
akostadinov

1
Це чудова відповідь. Однак це піддається багато гниття посилань. Я не знаю, куди ці ресурси, можливо, переїхали.
BlackVegetable

28

У Ruby 1.8 є лише зелені нитки, немає можливості створити справжню нитку на рівні ОС. Але, у ruby ​​1.9 буде нова функція під назвою волокна, яка дозволить вам створювати фактичні потоки на рівні ОС. На жаль, Ruby 1.9 все ще знаходиться в бета-версії, він планується стабільним через пару місяців.

Ще одна альтернатива - використовувати JRuby. JRuby реалізує потоки у вигляді головок на рівні ОС, в ньому немає "зелених ниток". Остання версія JRuby - 1.1.4 і еквівалентна Ruby 1.8


35
Неправда, що у Ruby 1.8 є лише зелені нитки, кілька реалізацій Ruby 1.8 мають вбудовані потоки: JRuby, XRuby, Ruby.NET та IronRuby. Волокна не дозволяють створювати нативні нитки, вони легші, ніж нитки. Насправді вони є напівкореспондентами, тобто є кооперативними.
Йорг W Міттаг

19
Я думаю, що з відповіді Джоша досить очевидно, що він має на увазі Ruby 1.8 під час виконання, він же МРТ, а не мова Ruby 1.8, коли він каже, що Ruby 1.8.
Тео

@Theo Також очевидно, що він псує поняття у своїй відповіді. Волокна - це не спосіб створити рідні нитки, як уже згадувалося, вони навіть більш легкі речі, ніж нитки, а в нинішньому крубі є рідні нитки, але з GIL.
Зоопарк Foo Bar

8

Це залежить від реалізації:

  • МРТ не має, YARV ближче.
  • JRuby і MacRuby мають.




У Ruby є закриття як і Blocks, так lambdasі Procs. Щоб повною мірою скористатися закриттями та декількома сердечниками в JRuby, виконавці Java пригодиться; для MacRuby мені подобаються черги GCD .

Зауважте, що можливість створення справжніх потоків на рівні ОС не означає, що ви можете використовувати декілька ядер процесора для паралельної обробки. Подивіться приклади нижче.

Це вихід простої програми Ruby, яка використовує 3 потоки за допомогою 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 

Як ви бачите тут, є чотири потоки ОС, однак працює лише одна зі станом R. Це пов’язано з обмеженням в реалізації реалізованих ниток Ruby.



Та сама програма, що зараз з JRuby. Ви можете бачити три потоки зі станом R, це означає, що вони працюють паралельно.

(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 


Та сама програма, що зараз з MacRuby. Є також три нитки, що працюють паралельно. Це відбувається тому, що потоки MacRuby - це потоки POSIX ( справжні потоки на рівні ОС ) і немає 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 


Знову ж програму, але тепер зі старим хорошим МРТ. Через те, що ця реалізація використовує зелені нитки, відображається лише одна нитка

(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



Якщо ви зацікавлені в багаторізковій нарізці Ruby, вам може бути цікавим мій звіт Налагодження паралельних програм за допомогою обробників виделок.
Для більш загального огляду внутрішніх приміщень Ruby Ruby під мікроскопом є корисним для читання.
Крім того, Ruby Threads та Global Interpreter Lock в C в Omniref пояснює у вихідному коді, чому нитки Ruby не працюють паралельно.


Під RMI ви маєте на увазі МРТ?
Маюреш Срівастава

4

Як щодо використання drb ? Це не справжня багатопотокова передача, а зв'язок між декількома процесами, але ви можете використовувати її зараз у 1.8, і це досить низьке тертя.


3

Я дозволю "Монітору системи" відповісти на це питання. Я виконую той самий код (нижче, який обчислює прості числа) з 8 потоками Ruby, що працюють на машині i7 (4 гіперпотоки) в обох випадках ... перший запуск:

jruby 1.5.6 (ruby 1.8.7 patchlevel 249) (2014-02-03 6586) (OpenJDK 64-бітний сервер VM 1.7.0_75) [amd64-java]

Другий з:

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

Цікаво, що процесор вище для потоків JRuby, але час для завершення трохи коротший для інтерпретованого Ruby. Це складно сказати з графіка, але другий (інтерпретований Ruby) пробіг використовує приблизно 1/2 ЦП (немає гіперточення?)

введіть тут опис зображення

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

Якщо ви використовуєте МРТ, то ви можете записати потоковий код у C як розширення або скориставшись коштовним каменем ruby-inline.


1

Якщо вам справді потрібен паралелізм у Ruby для системи рівня виробництва (де ви не можете використовувати бета-версію), процеси, ймовірно, є кращою альтернативою.
Але, напевно, спершу варто спробувати теми під JRuby.

Крім того, якщо вас цікавить майбутня нитка під Ruby, ця стаття може бути корисною.


JRuby - хороший варіант. Для паралельної обробки з використанням процесів, які мені подобаються github.com/grosser/parallel Parallel.map(['a','b','c'], :in_processes=>3){...
user454322


1

Оскільки не вдалося змінити цю відповідь, тож додайте сюди нову відповідь.

Оновлення (2017-05-08)

Ця стаття дуже стара, і інформація не відповідає поточному (2017) протектору, далі - додатки:

  1. Opal - це компілятор вихідного коду від джерела до JavaScript. Він також має реалізацію Ruby corelib, це поточний дуже активний розробник, і існує велика кількість (frontend) фреймворків, які працювали над ним. і виробництво готове. Оскільки базується на JavaScript, він не підтримує паралельні потоки.

  2. truffleruby - це високоефективна реалізація мови програмування Ruby. Побудований на GraalVM від Oracle Labs, TruffleRuby - це роздріб JRuby, поєднуючи його з кодом проекту Rubinius, а також містить код із стандартної реалізації Ruby, MRI, все ще живе розробка, не готова до виробництва. Ця версія рубіну, здається, народилася для продуктивності, я не знаю, чи підтримують паралельні потоки, але я думаю, що це повинно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.