У ruby есть реальная многопоточность?



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

1085   9  

9 ответов:

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

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

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

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

    его также иногда называют " МРТ "(для " Рубина маца Реализация"), Крубы или Мацрубы.

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

    однако, любое количество потоков C (потоки POSIX и т. д.) может работать параллельно с рубиновым потоком, так что внешние библиотеки C, или МРТ Расширения C, которые создают собственные потоки, все еще могут работать параллельный.

  2. вторая реализация ЯРЬВ (сокращение от " пока Еще одна рубиновая ВМ"). YARV реализует рубиновые потоки как POSIX или Windows NT Threads, однако, он использует глобальный переводчик Замок (Гил) для обеспечения этого только одна рубиновая нить может быть на самом деле запланированный в любое время.

    Как МРТ, C потоков можете на самом деле работают параллельно с рубиновыми потоками.

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

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

  4. XRuby и реализует Ruby Нити в качестве виртуальной машины Java темы. обновление: XRuby мертв.

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

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

  7. Рубиниусреализует рубиновые потоки как зеленые потоки в пределах своей виртуальной машины. Точнее: Рубиниус VM экспортирует очень легкий, очень гибкий параллелизм/параллелизм / нелокальная конструкция потока управления, называемая а"задание", и все другие конструкции параллелизма (потоки в это обсуждение, но и продолжения, актеры и другие вещи) реализованы в чистом Рубине, используя задачи.

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

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

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

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

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

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

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

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

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

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

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

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

  • у RMI нет, Ярв ближе.
  • JRuby и MacRuby выступает уже.




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

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

это выход простая Рубиновая программа, которая использует 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 (реальные потоки "на уровне ОС") и нет ГВЛ

(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 


Еще раз, та же программа, но теперь с добрым старым RMI. Из-за того, что эта реализация использует green-threads, появляется только один поток

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

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

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

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 процессоров (без гиперточности?)

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

Если вы используете МРТ, то вы можете написать резьбовой код в C либо в качестве расширения или с помощью ruby-inline gem.

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

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

вот некоторая информация о Rinda, которая является рубиновой реализацией Linda (parallel processing and distributed computing paradigm)http://charmalloc.blogspot.com/2009/12/linda-tuples-rinda-drb-parallel.html

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

обновление(2017-05-08)

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

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

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

Comments

    Ничего не найдено.