У ruby ​​есть реальная многопоточность? - PullRequest
284 голосов
/ 11 сентября 2008

Я знаю о "кооперативном" потоке рубинов, используя зеленые нити . Как я могу создать реальные потоки на уровне ОС в своем приложении, чтобы использовать для обработки несколько ядер ЦП?

Ответы [ 9 ]

599 голосов
/ 12 сентября 2008

Обновлено с комментариями Йорга в сентябре 2011 года

Вы, кажется, путаете две очень разные вещи здесь: Язык программирования Ruby и конкретная модель потоков конкретная реализация языка программирования Ruby. Там в настоящее время около 11 различных реализаций Ruby Язык программирования, с очень различными и уникальными потоками моделей.

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

  1. Первая реализация на самом деле не имеет имени, которое делает это довольно неловко ссылаться на него и действительно раздражает и сбивает с толку. Это чаще всего упоминается как "Рубин", который даже более раздражающим и запутанным, чем отсутствие имени, потому что это приводит к бесконечной путанице между особенностями Ruby Язык программирования и конкретная реализация Ruby.

    Его также иногда называют "МРТ" (для "Рубина Маца"). Реализация »), CRuby или MatzRuby.

    МРТ реализует рубиновые потоки как зеленые потоки внутри своих переводчик . К сожалению, это не позволяет эти темы чтобы быть запланированным параллельно, они могут запустить только один поток в время.

    Однако может выполняться любое количество потоков C (потоков POSIX и т. Д.) параллельно с Ruby Thread, поэтому внешние библиотеки C или MRI C Расширения, которые создают свои собственные потоки, все еще могут работать в параллельны друг другу.

  2. Вторая реализация - YARV (сокращение от "Пока" Еще одна Ruby VM "). YARV реализует Ruby Threads как POSIX или Windows NT Threads , однако, он использует глобальный интерпретатор Блокировка (GIL), чтобы гарантировать, что на самом деле может быть только один поток Ruby запланировано в любое время.

    Как и MRI, C Threads может фактически работать параллельно с Ruby Threads.

    В будущем возможно, что GIL может сломаться в более мелкозернистые замки, что позволяет все больше и больше код на самом деле работает параллельно, но это так далеко, это даже не запланировано еще.

  3. JRuby реализует рубиновые потоки как собственные потоки , где «нативные потоки» в случае JVM явно означают «JVM» Threads ". JRuby не накладывает на них дополнительной блокировки. Итак, могут ли эти потоки работать параллельно, зависит от JVM: некоторые JVM реализуют потоки JVM как потоки ОС, а некоторые как зеленые нити. (Основные JVM от Sun / Oracle используют исключительно потоки ОС начиная с JDK 1.3)

  4. XRuby также реализует Ruby Threads как потоки JVM . Обновление : XRuby мертв.

  5. IronRuby реализует рубиновые потоки как собственные потоки , где "Native Threads" в случае CLR явно означает "Темы CLR". IronRuby не накладывает на них дополнительной блокировки, поэтому они должны работать параллельно, пока ваш CLR поддерживает что.

  6. Ruby.NET также реализует Ruby Threads как CLR Темы . Обновление: Ruby.NET не работает.

  7. Рубиниус реализует рубиновые потоки как зеленые потоки в своей виртуальной машине . Точнее: Рубинус ВМ экспортирует очень легкий, очень гибкий конструкция параллелизма / параллелизма / нелокального потока управления, называется « Task » и все другие конструкции параллелизма (темы в это обсуждение, но также Продолжения , Актеры и другие вещи) реализованы в чистом Ruby, используя Задачи.

    Рубиниус не может (в настоящее время) планировать потоки параллельно, однако, добавив, что это не слишком большая проблема: Рубиниус может уже запускает несколько экземпляров ВМ в нескольких потоках POSIX в ппараллель , внутри одного процесса Рубиния. Так как темы фактически реализованные в Ruby, они могут, как и любой другой Ruby объект, быть сериализованным и отправленным на другую виртуальную машину в другом POSIX Тема. (Это та же модель ЛУЧ Эрланг ВМ использует для параллелизма SMP. Это уже реализовано для Рубиниус Актеры .)

    Обновление : Информация о Рубиниусе в этом ответе относится к дробовику ВМ, которого больше нет. «Новая» виртуальная машина C ++ не использует зеленые потоки, запланированные для нескольких виртуальных машин (т. Е. Стиль Erlang / BEAM), она использует более традиционную одиночную виртуальную машину с моделью с несколькими собственными потоками ОС, подобно той, что используется, скажем, в CLR, Mono и почти каждая JVM.

  8. MacRuby начинался как порт YARV на вершине Objective-C Runtime и CoreFoundation и Какао-фреймворки. Это в настоящее время значительно отличается от YARV, но AFAIK это в настоящее время still использует ту же модель потоков, что и YARV . Обновление: MacRuby зависит от сборщика мусора яблок, который объявлен устаревшим и будет удален в более поздних версиях MacOSX, MacRuby - нежить.

  9. Кардинал - Ruby-реализация для Parrot Виртуальная машина . Тем не менее, он не реализует потоки, когда это произойдет, он, вероятно, будет реализовывать их как Parrot Темы . Обновление : Кардинал кажется очень неактивным / мертвым.

  10. MagLev - это реализация Ruby для GemStone / S Smalltalk VM . У меня нет информации, какая модель потоков GemStone / S использует, какую модель потоков использует MagLev или даже если потоки даже реализованы (вероятно, нет).

  11. HotRuby - это , а не полная реализация его Ruby своя. Это реализация виртуальной машины YARV с байт-кодом в JavaScript. HotRuby не поддерживает темы (пока?) И когда это они не смогут работать параллельно, потому что JavaScript не поддерживает истинный параллелизм. Есть ActionScript версия HotRuby, однако, и ActionScript может на самом деле поддерживать параллелизм. Обновление : HotRuby мертв.

К сожалению, только две из этих 11 реализаций Ruby фактически готов к производству: МРТ и JRuby.

Итак, если вам нужны настоящие параллельные потоки, JRuby в данный момент является вашим единственный выбор - не то, чтобы это было плохо: JRuby на самом деле быстрее чем МРТ, и, возможно, более стабильным.

В противном случае «классическим» решением Ruby является использование процессов вместо потоков для параллелизма. Библиотека Ruby Core содержит Process модуль с Process.fork метод , который упрощает разветвление другого Ruby процесс. Кроме того, стандартная библиотека Ruby содержит Распределенная библиотека Ruby (dRuby / dRb) , которая позволяет Ruby код, который будет тривиально распределен по нескольким процессам, а не только на той же машине, но и по сети.

28 голосов
/ 11 сентября 2008

В Ruby 1.8 есть только зеленые потоки, нет способа создать настоящий поток на уровне ОС. Но в ruby ​​1.9 появится новая функция, называемая оптоволокнами, которая позволит вам создавать реальные потоки уровня ОС. К сожалению, Ruby 1.9 все еще находится в бета-версии, и через пару месяцев он должен стать стабильным.

Другая альтернатива - использовать JRuby. JRuby реализует потоки как темы уровня ОС, в них нет «зеленых потоков». Последняя версия JRuby является 1.1.4 и эквивалентна Ruby 1.8

8 голосов
/ 03 февраля 2014

Зависит от реализации:

  • МРТ не имеет, ЯРВ ближе.
  • JRuby и MacRuby имеют.




Руби имеет замыканий как 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 Under the Microscope - хорошее чтение.
Кроме того, потоки Ruby и глобальная блокировка интерпретатора в C в Omniref объясняют в исходном коде, почему потоки Ruby не работают параллельно.

4 голосов
/ 11 сентября 2008

Как насчет использования drb ? Это не настоящая многопоточность, а связь между несколькими процессами, но теперь вы можете использовать ее в 1.8, и это довольно низкое трение.

3 голосов
/ 23 февраля 2015

Я позволю «Системному монитору» ответить на этот вопрос. Я выполняю один и тот же код (ниже, который вычисляет простые числа) с 8-ю потоками Ruby, работающими на машине i7 (с 4-мя гиперпоточными ядрами) в обоих случаях ... первый запуск с:

jruby 1.5.6 (ruby 1.8.7, уровень исправления 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 процессоров (без гиперпоточности?)

enter image description here

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 голос
/ 08 мая 2017

Поскольку не удалось отредактировать этот ответ, добавьте новый ответ здесь.

Update (2017-05-08)

Эта статья очень старая, и информация не соответствует текущей (2017) протектор, Следующее дополнение:

  1. Opal - компилятор исходного кода в Ruby to JavaScript. Он также имеет реализацию Ruby corelib, которая в настоящее время является очень активным develompent, и над ней работает много (внешнего интерфейса) фреймворка. и производство готово. Поскольку он основан на JavaScript, он не поддерживает параллельные потоки.

  2. truffleruby - это высокопроизводительная реализация языка программирования Ruby. TruffleRuby, построенный на базе GraalVM Oracle Labs, является форком JRuby, объединяя его с кодом из проекта Rubinius, а также содержит код из стандартной реализации Ruby, MRI, все еще живая разработка, не готовая к работе. Эта версия ruby ​​кажется рожденной для производительности, я не знаю, поддерживают ли параллельные потоки, но я думаю, что это должно.

1 голос
/ 01 января 2010

Вот некоторая информация о Rinda, которая представляет собой реализацию Linda в Ruby (парадигма параллельной обработки и распределенных вычислений) http://charmalloc.blogspot.com/2009/12/linda-tuples-rinda-drb-parallel.html

1 голос
/ 11 сентября 2008

Если вам действительно нужен параллелизм в Ruby для системы уровня производства (где вы не можете использовать бета-версию), возможно, лучшей альтернативой будут процессы.
Но, прежде всего, стоит попробовать темы под JRuby.

Также, если вас интересует будущее многопоточности в Ruby, вам может пригодиться статья .

1 голос
/ 11 сентября 2008

Если вы используете MRI, то вы можете написать многопоточный код на C либо как расширение, либо используя гем ruby-inline.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...