Имеет ли рубин реальную многопоточность?

Я знаю о "кооперативной" резьбе рубина с помощью зеленых потоков. Как я могу создать реальные потоки "уровня OS" в моем приложении, чтобы использовать несколько ядер процессора для обработки?

Ответ 1

Обновлен с комментарием Jörg Sept 2011

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

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

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

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

    MRI реализует Ruby Threads как зеленые потоки в своей переводчик. К сожалению, он не позволяет этим потокам которые планируются параллельно, они могут запускать только один поток на время.

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

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

    Подобно MRI, C Threads могут работать параллельно с Ruby Threads.

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

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

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

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

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

  • Rubinius реализует Ruby Threads как зеленые темы в пределах своей виртуальной машины. Точнее: Рубиний VM экспортирует очень легкий, очень гибкий concurrency/parallelism/нелокальная конструкция управления потоком, называемая a " Task "и все остальные конструкторы concurrency (Темы в это обсуждение, но также Continuations, Актеры и другие вещи) реализованы в чистом Ruby, используя Задачи.

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

    Обновление. Информация о Rubinius в этом ответе касается виртуальной машины Shotgun, которая больше не существует. "Новая" С++ VM не использует зеленые потоки, запланированные для нескольких виртуальных машин (например, стиль Erlang/BEAM), использует более традиционную единую виртуальную машину с несколькими моделями потоков собственных ОС, так же как и используемая, скажем, CLR, Mono, и почти все JVM.

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

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

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

  • HotRuby - не полная реализация Ruby своя. Это реализация байт-кода YARV в JavaScript. HotRuby не поддерживает потоки (пока?), И когда он они не смогут работать параллельно, потому что JavaScript не поддерживает true parallelism. Существует ActionScript однако версия HotRuby, и ActionScript может на самом деле поддержка parallelism. Обновить: HotRuby мертв.

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

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

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

Ответ 2

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

Другой вариант - использовать JRuby. JRuby реализует потоки как теги уровня OS, в ней нет "зеленых потоков". Последняя версия JRuby - 1.1.4 и эквивалентна Ruby 1.8

Ответ 3

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

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




Руби имеет закрытие в виде Blocks, lambdas и Procs. Чтобы воспользоваться всеми преимуществами замыканий и нескольких ядер в JRuby, Java-исполнители пригодятся; для MacRuby мне нравятся очереди GCD.

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

Это результат простой программы на Ruby, которая использует 3 потока с использованием Ruby 2.1.0:

([email protected] ~)$ 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, что означает, что они работают параллельно.

([email protected] ~)$ 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 отсутствует

([email protected] ~)$ 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 


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

([email protected] ~)$ 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 a Microscope.
Кроме того, Ruby Threads и Global Interpreter Lock в C в Omniref объясняют в исходном коде, почему потоки Ruby не работают параллельно.

Ответ 4

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

Ответ 5

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

jruby 1.5.6 (ruby 1.8.7 patchlevel 249) (2014-02-03 6586) (64-разрядный сервер OpenJDK VM 1.7.0_75) [amd64-java]

Второй вариант:

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

Интересно, что процессор для потоков JRuby выше, но время для его завершения немного короче для интерпретируемого Ruby. Это сложно сказать по графику, но второй (интерпретируемый Ruby) запуск использует около 1/2 CPU (без гиперпотока?)

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

Ответ 6

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

Ответ 7

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

Также, если вас интересует будущее потоковой передачи в Ruby, вы можете найти эту статью полезной.

Ответ 8

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

Ответ 9

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

Обновить (2017-05-08)

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

  1. Opal - это источник от источника к исходному коду Ruby to JavaScript. В нем также реализована Ruby corelib, она очень активно развивается и существует много (frontend) рамок, которые над ней работали. и производство готово. Поскольку база на javascript не поддерживает параллельные потоки.

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