답변:
Jörg의 2011 년 9 월 코멘트로 업데이트 됨
여기에 Ruby 프로그래밍 언어와 Ruby 프로그래밍 언어의 특정 구현에 대한 특정 스레딩 모델이라는 두 가지 매우 혼란스러운 내용이 있습니다. 현재 매우 다른 고유 스레딩 모델 을 사용하여 약 11 가지의 Ruby 프로그래밍 언어 구현이 있습니다.
(불행하게도, 그 (11 개)의 구현 만이 실제로 생산 사용에 대한 준비가되어 있지만, 올해 말까지 수는 아마 네다섯까지 갈 것입니다.) ( 업데이트 : MRI, JRuby를, YARV (통역 : 지금은 5입니다 Ruby 1.9), Rubinius 및 IronRuby).
첫 번째 구현에는 실제로 이름이 없으므로 참조하기가 상당히 어려워지고 실제로 성 가시고 혼란 스럽습니다. 루비 프로그래밍 언어의 기능과 특정 루비 구현 사이의 끝없는 혼란을 초래하기 때문에 이름이없는 것보다 훨씬 더 성 가시고 혼란스러운 "루비"라고합니다.
"MRI"( "Matz 's Ruby Implementation"), CRuby 또는 MatzRuby라고도합니다.
MRI는 인터프리터 내에서 Ruby Threads를 Green Threads로 구현 합니다. 불행히도 스레드를 병렬로 예약 할 수 없으며 한 번에 하나의 스레드 만 실행할 수 있습니다.
그러나 여러 C 스레드 (POSIX 스레드 등)를 Ruby 스레드와 병렬로 실행할 수 있으므로 자체 스레드를 작성하는 외부 C 라이브러리 또는 MRI C 확장은 여전히 병렬로 실행될 수 있습니다.
두 번째 구현은 YARV ( "Yet Another Ruby VM"의 약자)입니다. YARV는 Ruby 스레드를 POSIX 또는 Windows NT 스레드로 구현 하지만 GIL (Global Interpreter Lock)을 사용하여 한 번에 하나의 Ruby 스레드 만 실제로 스케줄 할 수 있습니다.
MRI와 마찬가지로 C 스레드 는 실제로 Ruby 스레드와 병렬로 실행될 수 있습니다 .
앞으로 GIL 이 더 세밀한 잠금으로 분류되어 점점 더 많은 코드가 실제로 병렬로 실행될 수 있지만 아직 멀었으므로 아직 계획 되지 않았습니다.
JRuby 는 Ruby Threads를 Native Threads로 구현합니다 . 여기서 JVM의 경우 "Native Threads"는 분명히 "JVM Threads"를 의미합니다. JRuby는 추가 잠금을 부과하지 않습니다. 따라서 해당 스레드가 실제로 병렬로 실행될 수 있는지 여부는 JVM에 따라 다릅니다. 일부 JVM은 JVM 스레드를 OS 스레드로 구현하고 일부는 녹색 스레드로 구현합니다. (Sun / Oracle의 주류 JVM은 JDK 1.3부터 독점적으로 OS 스레드를 사용합니다)
XRuby 는 Ruby 스레드를 JVM 스레드로 구현 합니다. 업데이트 : XRuby가 종료되었습니다.
IronRuby 는 Ruby Threads를 Native Threads로 구현합니다 . 여기서 CLR의 경우 "Native Threads"는 분명히 "CLR Threads"를 의미합니다. IronRuby는 추가 잠금을 부과하지 않으므로 CLR이 지원하는 한 병렬로 실행해야합니다.
Ruby.NET 은 또한 Ruby Threads를 CLR Threads로 구현 합니다. 업데이트 : Ruby.NET이 종료되었습니다.
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.
MacRuby 는 Objective-C Runtime과 CoreFoundation 및 Cocoa Framework를 기반으로 YARV 포트로 시작했습니다. 이제 YARV에서 크게 분기되었지만 AFAIK는 현재 여전히 YARV와 동일한 스레딩 모델을 공유합니다 . 업데이트 : MacRuby는 사과 가비지 수집기에 종속되어 있으며 더 이상 사용되지 않으며 MacOSX의 이후 버전에서 제거되며 MacRuby는 언데드입니다.
카디널 은 앵무새 가상 머신을 위한 루비 구현입니다 . 그것은하면, 그것은 아마로 구현됩니다, 그러나, 아직 스레드를 구현하지 않습니다 앵무새 스레드 . 업데이트 : 추기경은 매우 비활성 / 죽은 것처럼 보입니다.
MagLev 는 GemStone / S Smalltalk VM을 위한 Ruby 구현입니다 . GemStone / S가 사용하는 스레딩 모델, MagLev가 사용하는 스레딩 모델 또는 스레드가 아직 구현 된 경우 (아마도)에 대한 정보가 없습니다.
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
Ruby 1.8에는 녹색 스레드 만 있으며 실제 "OS 레벨"스레드를 작성할 수있는 방법이 없습니다. 그러나 루비 1.9에는 파이버라는 새로운 기능이있어 실제 OS 레벨 스레드를 만들 수 있습니다. 불행히도 Ruby 1.9는 아직 베타 버전이며 몇 달 안에 안정적으로 계획되어 있습니다.
다른 대안은 JRuby를 사용하는 것입니다. JRuby는 스레드를 OS 레벨 thead로 구현하며 "그린 스레드"는 없습니다. JRuby의 최신 버전은 1.1.4이며 Ruby 1.8과 같습니다.
구현에 따라 다릅니다.
루비는 , 및 로 닫힙니다 . JRuby에서 클로저 및 다중 코어를 최대한 활용하기 위해 Java 실행 프로그램 이 유용합니다. MacRuby의 경우 GCD 대기열을 좋아 합니다.
실제 "OS 수준"스레드
를 만들 수 있다는 점에 유의하십시오.Blocks
lambdas
Procs
병렬 처리에 여러 개의 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 스레드가 병렬로 실행되지 않는 이유를 소스 코드에 설명합니다.
"시스템 모니터"가이 질문에 대답하도록하겠습니다. 두 경우 모두 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
MRI를 사용하는 경우 C에서 스레드 코드를 확장 또는 루비 인라인 gem을 사용하여 작성할 수 있습니다.
프로덕션 레벨 시스템 (베타를 사용할 수없는)에 대해 Ruby에서 병렬 처리가 실제로 필요한 경우 프로세스가 더 나은 대안 일 수 있습니다.
그러나 먼저 JRuby에서 스레드를 시도해 볼 가치가 있습니다.
또한 루비에서 쓰레딩에 관심이 있다면이 기사가 유용 할 것입니다.
Parallel.map(['a','b','c'], :in_processes=>3){...
해당 답변을 수정할 수 없으므로 여기에 새 답변을 추가하십시오.
업데이트 (2017-05-08)
이 기사는 매우 오래되었으며 정보는 현재 (2017) 트레드를 따르지 않습니다. 다음은 일부 보충 사항입니다.
Opal 은 Ruby to JavaScript 소스-소스 컴파일러입니다. 또한 Ruby corelib의 구현을 가지고 있으며, 현재 매우 활발한 개발자이며, (frontend) 프레임 워크가 존재합니다. 그리고 생산 준비. 자바 스크립트를 기반으로하기 때문에 병렬 스레드를 지원하지 않습니다.
truffleruby 는 Ruby 프로그래밍 언어의 고성능 구현입니다. Oracle Labs의 GraalVM을 기반으로하는 TruffleRuby는 JRuby의 포크이며,이를 Rubinius 프로젝트의 코드와 결합하고 Ruby의 표준 구현 인 MRI의 표준 구현 코드를 포함하고 있습니다. 이 버전의 루비는 성능을 위해 태어난 것처럼 보입니다. 병렬 스레드를 지원하는지 모르겠지만 그래야한다고 생각합니다.