루비에 실제 멀티 스레딩이 있습니까?


295

나는 녹색 스레드를 사용하는 루비의 "협동"스레딩에 대해 알고 있습니다. 처리에 여러 CPU 코어를 사용하기 위해 애플리케이션에서 실제 "OS 레벨"스레드를 작성하려면 어떻게해야합니까?

답변:


612

Jörg의 2011 년 9 월 코멘트로 업데이트 됨

여기에 Ruby 프로그래밍 언어와 Ruby 프로그래밍 언어의 특정 구현에 대한 특정 스레딩 모델이라는 두 가지 매우 혼란스러운 내용이 있습니다. 현재 매우 다른 고유 스레딩 모델 을 사용하여 약 11 가지의 Ruby 프로그래밍 언어 구현이 있습니다.

(불행하게도, 그 (11 개)의 구현 만이 실제로 생산 사용에 대한 준비가되어 있지만, 올해 말까지 수는 아마 네다섯까지 갈 것입니다.) ( 업데이트 : MRI, JRuby를, YARV (통역 : 지금은 5입니다 Ruby 1.9), Rubinius 및 IronRuby).

  1. 첫 번째 구현에는 실제로 이름이 없으므로 참조하기가 상당히 어려워지고 실제로 성 가시고 혼란 스럽습니다. 루비 프로그래밍 언어의 기능과 특정 루비 구현 사이의 끝없는 혼란을 초래하기 때문에 이름이없는 것보다 훨씬 더 성 가시고 혼란스러운 "루비"라고합니다.

    "MRI"( "Matz 's Ruby Implementation"), CRuby 또는 MatzRuby라고도합니다.

    MRI는 인터프리터 내에서 Ruby Threads를 Green Threads로 구현 합니다. 불행히도 스레드를 병렬로 예약 할 수 없으며 한 번에 하나의 스레드 만 실행할 수 있습니다.

    그러나 여러 C 스레드 (POSIX 스레드 등)를 Ruby 스레드와 병렬로 실행할 수 있으므로 자체 스레드를 작성하는 외부 C 라이브러리 또는 MRI C 확장은 여전히 ​​병렬로 실행될 수 있습니다.

  2. 두 번째 구현은 YARV ( "Yet Another Ruby VM"의 약자)입니다. YARV는 Ruby 스레드를 POSIX 또는 Windows NT 스레드로 구현 하지만 GIL (Global Interpreter Lock)을 사용하여 한 번에 하나의 Ruby 스레드 만 실제로 스케줄 할 수 있습니다.

    MRI와 마찬가지로 C 스레드 는 실제로 Ruby 스레드와 병렬로 실행될 수 있습니다 .

    앞으로 GIL 이 더 세밀한 잠금으로 분류되어 점점 더 많은 코드가 실제로 병렬로 실행될 수 있지만 아직 멀었으므로 아직 계획 되지 않았습니다.

  3. JRuby 는 Ruby Threads를 Native Threads로 구현합니다 . 여기서 JVM의 경우 "Native Threads"는 분명히 "JVM Threads"를 의미합니다. JRuby는 추가 잠금을 부과하지 않습니다. 따라서 해당 스레드가 실제로 병렬로 실행될 수 있는지 여부는 JVM에 따라 다릅니다. 일부 JVM은 JVM 스레드를 OS 스레드로 구현하고 일부는 녹색 스레드로 구현합니다. (Sun / Oracle의 주류 JVM은 JDK 1.3부터 ​​독점적으로 OS 스레드를 사용합니다)

  4. XRuby 는 Ruby 스레드를 JVM 스레드로 구현 합니다. 업데이트 : XRuby가 종료되었습니다.

  5. IronRuby 는 Ruby Threads를 Native Threads로 구현합니다 . 여기서 CLR의 경우 "Native Threads"는 분명히 "CLR Threads"를 의미합니다. IronRuby는 추가 잠금을 부과하지 않으므로 CLR이 지원하는 한 병렬로 실행해야합니다.

  6. Ruby.NET 은 또한 Ruby Threads를 CLR Threads로 구현 합니다. 업데이트 : Ruby.NET이 종료되었습니다.

  7. Rubinius 는 가상 머신 내에서 Ruby Threads를 Green Threads로 구현 합니다. 보다 정확하게 : Rubinius VM은 " 작업 " 이라고하는 매우 가볍고 매우 유연한 동시성 / 병렬 / 비 로컬 제어 흐름 구성 및 기타 모든 동시성 구성 (이 논의에서는 스레드뿐만 아니라 연속성 , 액터 및 기타 항목)을 내 보냅니다. )는 Tasks를 사용하여 순수한 Ruby로 구현됩니다.

    Rubinius는 스레드를 병렬로 (현재) 예약 할 수 없지만 너무 큰 문제는 아닙니다. Rubinius는 한 Rubinius 프로세스 내 에서 여러 POSIX 스레드의 여러 VM 인스턴스를 병렬로 실행할 수 있습니다 . 스레드는 실제로 루비로 구현되므로 다른 루비 객체와 마찬가지로 직렬화되어 다른 POSIX 스레드의 다른 VM으로 전송 될 수 있습니다. BEAM Erlang VM이 SMP 동시성에 사용 하는 것과 동일한 모델 입니다. 이미 Rubinius Actors에 구현되어 있습니다.

    업데이트 :이 답변의 Rubinius에 대한 정보는 더 이상 존재하지 않는 Shotgun VM에 관한 것입니다. "새로운"C ++ VM은 여러 VM (예 : Erlang / BEAM 스타일)에서 예약 된 녹색 스레드를 사용하지 않습니다. 예를 들어 CLR, Mono에서 사용하는 것과 같은 여러 고유 OS 스레드 모델이있는보다 전통적인 단일 VM을 사용합니다. 그리고 거의 모든 JVM.

  8. MacRuby 는 Objective-C Runtime과 CoreFoundation 및 Cocoa Framework를 기반으로 YARV 포트로 시작했습니다. 이제 YARV에서 크게 분기되었지만 AFAIK는 현재 여전히 YARV와 동일한 스레딩 모델을 공유합니다 . 업데이트 : MacRuby는 사과 가비지 수집기에 종속되어 있으며 더 이상 사용되지 않으며 MacOSX의 이후 버전에서 제거되며 MacRuby는 언데드입니다.

  9. 카디널 은 앵무새 가상 머신을 위한 루비 구현입니다 . 그것은하면, 그것은 아마로 구현됩니다, 그러나, 아직 스레드를 구현하지 않습니다 앵무새 스레드 . 업데이트 : 추기경은 매우 비활성 / 죽은 것처럼 보입니다.

  10. MagLev 는 GemStone / S Smalltalk VM을 위한 Ruby 구현입니다 . GemStone / S가 사용하는 스레딩 모델, MagLev가 사용하는 스레딩 모델 또는 스레드가 아직 구현 된 경우 (아마도)에 대한 정보가 없습니다.

  11. HotRuby 는 자체 루비 구현 이 아닙니다 . JavaScript로 YARV 바이트 코드 VM을 구현 한 것입니다. HotRuby는 스레드 (아직)를 지원하지 않으며, 그렇지 않으면 JavaScript가 진정한 병렬 처리를 지원하지 않기 때문에 병렬로 실행할 수 없습니다. ActionScript 버전의 HotRuby가 있지만 ActionScript는 실제로 병렬 처리를 지원할 수 있습니다. 업데이트 : HotRuby가 종료되었습니다.

불행히도이 11 가지 Ruby 구현 중 두 가지만 실제로 프로덕션 준비가되었습니다 : MRI 및 JRuby.

따라서 진정한 병렬 스레드를 원한다면 현재 JRuby가 유일한 선택입니다. JRuby는 실제로 MRI보다 빠르며 더 안정적입니다.

그렇지 않으면 "클래식"루비 솔루션은 병렬 처리를 위해 스레드 대신 프로세스를 사용하는 것입니다. Ruby Core Library에는 다른 Ruby 프로세스를 쉽게 제거 할 수 있는 메소드 가 포함 된 Process모듈 이 포함되어 있습니다 . 또한 Ruby 표준 라이브러리에는 분산 루비 (dRuby / dRb) 라이브러리가 포함되어있어 동일한 코드뿐만 아니라 네트워크에서도 여러 프로세스에 루비 코드를 간단하게 배포 할 수 있습니다.Process.fork


1
하지만 포크를 사용하면 jruby에서 사용법이 중단됩니다 ...
— akostadinov

1
이것은 좋은 대답입니다. 그러나 링크 썩음이 많이 발생합니다. 이 리소스가 어디로 옮겼는지 모르겠습니다.
— BlackVegetable

28

Ruby 1.8에는 녹색 스레드 만 있으며 실제 "OS 레벨"스레드를 작성할 수있는 방법이 없습니다. 그러나 루비 1.9에는 파이버라는 새로운 기능이있어 실제 OS 레벨 스레드를 만들 수 있습니다. 불행히도 Ruby 1.9는 아직 베타 버전이며 몇 달 안에 안정적으로 계획되어 있습니다.

다른 대안은 JRuby를 사용하는 것입니다. JRuby는 스레드를 OS 레벨 thead로 구현하며 "그린 스레드"는 없습니다. JRuby의 최신 버전은 1.1.4이며 Ruby 1.8과 같습니다.


35
Ruby 1.8에는 녹색 스레드 만 있고 Ruby 1.8의 여러 구현에는 기본 스레드가 있습니다. JRuby, XRuby, Ruby.NET 및 IronRuby입니다. 파이버는 기본 스레드를 만들 수 없으며 스레드 보다 가볍 습니다. 그들은 실제로 세미 코 루틴, 즉 협동 적입니다.
— Jörg W Mittag

19
Josh의 대답에서 Ruby 1.8이 아니라면 Ruby 1.8이 아니라 런타임 인 ​​Ruby 1.8 런타임 (일명 MRI)을 의미한다는 것이 명백합니다.
— Theo

@Theo 또한 그가 대답에서 개념을 망친다는 것도 분명합니다. 파이버는 이미 언급했듯이 네이티브 스레드를 생성하는 방법이 아니며 스레드보다 훨씬 경량이며 현재 cruby에는 네이티브 스레드가 있지만 GIL이 있습니다.
— 푸 바 동물원

8

구현에 따라 다릅니다.

  • MRI에는 없습니다. YARV가 더 가깝습니다.
  • JRuby와 MacRuby가 있습니다.




루비는 , 및 로 닫힙니다 . JRuby에서 클로저 및 다중 코어를 최대한 활용하기 위해 Java 실행 프로그램 이 유용합니다. MacRuby의 경우 GCD 대기열을 좋아 합니다. 실제 "OS 수준"스레드 를 만들 수 있다는 점에 유의하십시오.BlockslambdasProcs

병렬 처리에 여러 개의 CPU 코어를 사용할 수있는 것은 아닙니다. 아래 예를보십시오.

다음은 Ruby 2.1.0을 사용하여 3 개의 스레드 를 사용하는 간단한 Ruby 프로그램 의 출력입니다 .

(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 

여기에서 볼 수 있듯이 4 개의 OS 스레드가 있지만 상태 R가 있는 스레드 만 실행됩니다. 이것은 Ruby의 스레드가 구현되는 방식에 한계가 있기 때문입니다.



JRuby와 동일한 프로그램. state와 함께 세 개의 스레드를 볼 수 있습니다 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 스레드입니다 ( 진짜 "OS 수준"스레드 )와이없는 더 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 


다시 한번, 같은 프로그램이지만 지금은 좋은 MRI를 가지고 있습니다. 이 구현은 그린 스레드를 사용하기 때문에 하나의 스레드 만 나타납니다.

(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 a Microscope 를 잘 읽어보십시오.
또한 Omniref의 C 에서 Ruby Threads와 Global Interpreter Lock은 Ruby 스레드가 병렬로 실행되지 않는 이유를 소스 코드에 설명합니다.


RMI는 MRI를 의미합니까?
— Mayuresh Srivastava

4

drb 를 사용하는 것은 어떻 습니까? 실제 멀티 스레딩이 아니라 여러 프로세스 간의 통신이지만 1.8에서 사용할 수 있으며 마찰이 적습니다.


3

"시스템 모니터"가이 질문에 대답하도록하겠습니다. 두 경우 모두 i7 (4 하이퍼 스레드 코어) 컴퓨터에서 8 개의 Ruby 스레드가 실행되는 동일한 코드 (아래의 소수를 계산)를 실행하고 있습니다. 첫 번째 실행은 다음과 같습니다.

jruby 1.5.6 (ruby 1.8.7 patchlevel 249) (2014-02-03 6586) (OpenJDK 64 비트 서버 VM 1.7.0_75) [amd64-java]

두 번째는 다음과 같습니다.

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

흥미롭게도 JRuby 스레드의 CPU는 높지만 해석되는 Ruby의 완료 시간은 약간 짧습니다. 그래프에서 말하기는 어렵지만 두 번째 (해석 된 Ruby) 실행은 CPU의 약 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

MRI를 사용하는 경우 C에서 스레드 코드를 확장 또는 루비 인라인 gem을 사용하여 작성할 수 있습니다.


1

프로덕션 레벨 시스템 (베타를 사용할 수없는)에 대해 Ruby에서 병렬 처리가 실제로 필요한 경우 프로세스가 더 나은 대안 일 수 있습니다.
그러나 먼저 JRuby에서 스레드를 시도해 볼 가치가 있습니다.

또한 루비에서 쓰레딩에 관심이 있다면이 기사가 유용 할 것입니다.


JRuby는 좋은 옵션입니다. 등을 이용한 병렬 처리 공정 용 I github.com/grosser/parallel Parallel.map(['a','b','c'], :in_processes=>3){...
— user454322

1

Linda의 병렬 구현 및 분산 컴퓨팅 패러다임 인 Rinda에 대한 일부 정보는 다음과 같습니다 .


1

해당 답변을 수정할 수 없으므로 여기에 새 답변을 추가하십시오.

업데이트 (2017-05-08)

이 기사는 매우 오래되었으며 정보는 현재 (2017) 트레드를 따르지 않습니다. 다음은 일부 보충 사항입니다.

  1. Opal 은 Ruby to JavaScript 소스-소스 컴파일러입니다. 또한 Ruby corelib의 구현을 가지고 있으며, 현재 매우 활발한 개발자이며, (frontend) 프레임 워크가 존재합니다. 그리고 생산 준비. 자바 스크립트를 기반으로하기 때문에 병렬 스레드를 지원하지 않습니다.

  2. truffleruby 는 Ruby 프로그래밍 언어의 고성능 구현입니다. Oracle Labs의 GraalVM을 기반으로하는 TruffleRuby는 JRuby의 포크이며,이를 Rubinius 프로젝트의 코드와 결합하고 Ruby의 표준 구현 인 MRI의 표준 구현 코드를 포함하고 있습니다. 이 버전의 루비는 성능을 위해 태어난 것처럼 보입니다. 병렬 스레드를 지원하는지 모르겠지만 그래야한다고 생각합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.