Ruby'nin gerçek çoklu iş parçacığı var mı?


295

Yeşil iplikler kullanarak yakut "kooperatif" iplik biliyorum . İşleme için birden çok işlemci çekirdeği kullanmak amacıyla uygulamamda nasıl gerçek "İşletim Sistemi düzeyinde" iş parçacıkları oluşturabilirim?

Yanıtlar:


612

Jörg'ün Eylül 2011 yorumuyla güncellendi

İki kafa karıştırıcı gibi görünüyor çok burada farklı şeyler: Dil ve Ruby Programlama Dili Bir özel uygulamada belirli iş parçacığı modeli Programlama Yakut. Şu anda Ruby Programlama Dili'nin çok farklı ve benzersiz diş açma modelleri ile yaklaşık 11 farklı uygulaması vardır .

(Ne yazık ki, bu 11 uygulamadan sadece ikisi üretim için hazırdır, ancak yıl sonuna kadar bu sayı muhtemelen dört veya beşe kadar çıkacaktır.) ( Güncelleme : şimdi 5: MRI, JRuby, YARV (yorumlayıcı) Ruby 1.9 için), Rubinius ve IronRuby).

  1. İlk uygulamanın aslında bir adı yoktur, bu da onu ifade etmeyi oldukça garip hale getirir ve gerçekten sinir bozucu ve kafa karıştırıcıdır. Çoğu zaman "Ruby" olarak adlandırılır, bu da isimsiz olmaktan daha rahatsız edici ve kafa karıştırıcıdır, çünkü Ruby Programlama Dilinin özellikleri ile belirli bir Ruby Uygulaması arasında sonsuz karışıklığa yol açar.

    Ayrıca bazen "MRI" ("Matz'un Ruby Uygulaması" için), CRuby veya MatzRuby olarak da adlandırılır.

    MRG, Ruby Threads'ı yorumlayıcısında Green Threads olarak uygular . Ne yazık ki, bu iş parçacıklarının paralel olarak zamanlanmasına izin vermez, aynı anda yalnızca bir iş parçacığı çalıştırabilir.

    Ancak, herhangi bir sayıda C İş Parçacığı (POSIX İş Parçacığı vb.) Ruby İş Parçacığına paralel çalışabilir, bu nedenle kendi iş parçacıklarını oluşturan dış C Kütüphaneleri veya MRI C Uzantıları paralel olarak çalışabilir.

  2. İkinci uygulama YARV ("Yet Another Ruby VM" kısaltması). YARV, Ruby Threads'i POSIX veya Windows NT Threads olarak uygular , ancak aynı anda yalnızca bir Ruby Thread'in zamanlanabilmesini sağlamak için Global Tercüman Kilidi (GIL) kullanır.

    MR gibi, C Konular olabilir aslında Yakut Konular paralel.

    Gelecekte, bu GIL ki mümkündür belki böylece daha kod aslında paralel olarak çalışmasına izin veren, daha ince taneli kilitleri bölünmüş olsun, ama bu bile değil, çok uzak planlı henüz.

  3. JRuby, Ruby Threads'ı Native Threads olarak uygular ; burada JVM durumunda "Native Threads" açıkça "JVM Threads" anlamına gelir. JRuby bunlara ek bir kilit uygulamaz. Bu nedenle, bu iş parçacıklarının gerçekte paralel çalışıp çalışamayacağı JVM'ye bağlıdır: bazı JVM'ler JVM İş Parçacıklarını OS İş Parçacığı, bazıları Yeşil İş Parçacığı olarak uygular. (Sun / Oracle'ın genel JVM'leri, JDK 1.3'ten beri yalnızca işletim sistemi iş parçacıklarını kullanır)

  4. XRuby ayrıca Ruby Threads'ı JVM Threads olarak uygular . Güncelleme : XRuby öldü.

  5. IronRuby Ruby İş Parçacıklarını Yerel İş Parçacığı olarak uygular ; burada CLR durumunda "Yerel İş Parçacıkları" açıkça "CLR İş Parçacıkları" anlamına gelir. IronRuby onlara ek bir kilitleme yapmaz, bu nedenle CLR'niz bunu desteklediği sürece paralel olarak çalışmalıdırlar.

  6. Ruby.NET, Ruby Konularını CLR Konuları olarak da uygular . Güncelleme: Ruby.NET öldü.

  7. Rubinius , Sanal Makinasında Ruby İplerini Yeşil İplikler olarak uygular . Daha doğrusu: Rubinius VM, " Görev " adı verilen çok hafif, çok esnek bir eşzamanlılık / paralellik / yerel olmayan kontrol akışı yapısı ve diğer tüm eşzamanlılık yapılarını dışa aktarır (Bu tartışmadaki konular, aynı zamanda Devamlar , Aktörler ve diğer şeyler) ) Görevler kullanılarak saf Ruby'de uygulanır.

    Rubinius (şu anda) İş Parçacıklarını paralel olarak zamanlayamaz, ancak bunun çok fazla sorun olmadığını da ekler: Rubinius, bir Rubinius işleminde paralel olarak birkaç POSIX İş Parçasında birkaç VM örneği çalıştırabilir . Konular gerçekten Ruby'de uygulandığından, diğer Ruby nesneleri gibi serileştirilebilir ve farklı bir POSIX İş Parçasındaki farklı bir VM'ye gönderilebilirler. (Bu BEAM Erlang VM'nin SMP eşzamanlılık için kullandığı modelle aynıdır . Zaten Rubinius Aktörleri için uygulanmıştır .)

    Güncelleme : Bu cevapta Rubinius hakkındaki bilgiler, artık mevcut olmayan Shotgun VM hakkındadır. "Yeni" C ++ VM, birden çok VM'de planlanan yeşil iş parçacıklarını (yani Erlang / BEAM stili) kullanmaz, tıpkı CLR, Mono tarafından kullanılan gibi, birden çok yerel işletim sistemi iş parçacığı modeliyle daha geleneksel bir tek VM kullanır. ve hemen hemen her JVM'de.

  8. MacRuby , Objective-C Çalışma Zamanı ve CoreFoundation ve Cocoa Frameworks üzerinde YARV limanı olarak başladı. Şimdi YARV'dan önemli ölçüde sapmıştır, ancak AFAIK şu anda YARV ile aynı Diş Açma Modelini paylaşmaktadır . Güncelleme: MacRuby, kullanımdan kaldırıldığı bildirilen ve MacOSX'in sonraki sürümlerinde kaldırılacak olan elma çöp toplayıcısına bağlıdır, MacRuby ölümsüzdür.

  9. Cardinal , Parrot Sanal Makinesi için Ruby uygulamasıdır . Henüz iş parçacığı uygulamaz, ancak bunu yaparken muhtemelen Parrot Threads olarak uygular . Güncelleme : Kardinal çok inaktif / ölü görünüyor.

  10. MagLev , GemStone / S Smalltalk VM için bir Ruby Uygulamasıdır . GemStone / S'nin hangi diş açma modelini kullandığını, MagLev'in hangi diş açma modelini kullandığını veya iş parçacıkları henüz uygulanmış olsa bile (muhtemelen değil) hiçbir bilgim yok.

  11. HotRuby , kendi başına tam bir Ruby Uygulaması değildir . JavaScript'te bir YARV bayt kodu VM'sinin bir uygulamasıdır. HotRuby iş parçacıklarını desteklemiyor (henüz?) Ve çalıştığında, paralel çalışamazlar, çünkü JavaScript'in gerçek paralellik için desteği yoktur. Bununla birlikte, HotRuby'nin bir ActionScript sürümü vardır ve ActionScript aslında paralelliği destekleyebilir. Güncelleme : HotRuby öldü.

Ne yazık ki, bu 11 Ruby Uygulamasından sadece ikisi üretime hazırdır: MR ve JRuby.

Yani, gerçek paralel dişler istiyorsanız, JRuby şu anda tek seçeneğinizdir - bu kötü bir şey değil: JRuby aslında MRI'dan daha hızlı ve tartışmasız daha kararlı.

Aksi takdirde, "klasik" Ruby çözümü paralellik için iplik yerine işlemleri kullanmaktır. Yakut Çekirdek Kitaplığı içeren Processmodül ile Process.fork yöntemle ölü kolay başka Yakut sürecini çatal kolaylaştırır. Ayrıca, Ruby Standart Kütüphanesi, Ruby kodunun yalnızca aynı makinede değil, aynı zamanda ağ üzerinde de birden fazla işlem arasında önemsiz bir şekilde dağıtılmasına izin veren Dağıtılmış Ruby (dRuby / dRb) kütüphanesini içerir.


1
ama çatal kullanarak jruby kullanımını kıracak ... sadece söyleyerek
akostadinov

1
Bu harika bir cevap. Ancak bir çok bağlantı çürümesine maruz kalır. Bu kaynakların nereye taşınmış olabileceğini bilmiyorum.
BlackVegetable

28

Ruby 1.8 yalnızca yeşil iş parçacıklarına sahiptir, gerçek bir "OS düzeyinde" iş parçacığı oluşturmanın bir yolu yoktur. Ancak, ruby ​​1.9, gerçek OS düzeyinde iş parçacıkları oluşturmanıza olanak tanıyan lif adı verilen yeni bir özelliğe sahip olacak. Ne yazık ki, Ruby 1.9 hala beta aşamasında, birkaç ay içinde kararlı olması planlanıyor.

Başka bir alternatif JRuby kullanmaktır. JRuby iş parçacığı OS düzeyinde iplikler olarak uygular, içinde "yeşil iplik" yoktur. JRuby'nin son sürümü 1.1.4 ve Ruby 1.8'e eşdeğer


35
Ruby 1.8'in sadece yeşil iş parçacığı olduğu yanlıştır, Ruby 1.8'in bazı uygulamalarında yerel iş parçacıkları vardır: JRuby, XRuby, Ruby.NET ve IronRuby. Lifler doğal ipliklerin oluşturulmasına izin vermez, ipliklerden daha hafiftir . Aslında yarı koroutinler, yani kooperatifler.
Jörg W Mittag

19
Josh'un cevabından, Ruby 1.8 dediği zaman, dilin Ruby 1.8'i değil, çalışma zamanı Ruby 1.8 anlamına geldiğini düşünüyorum.
Theo

@Theo Aynı zamanda cevabında kavramları karıştırdığı da açıktır. Lifler, daha önce de belirtildiği gibi, yerel iplikler oluşturmanın bir yolu değildir, ipliklerden daha hafif şeylerdir ve mevcut krizin doğal iplikleri vardır, ancak GIL ile.
Foo Bar Hayvanat Bahçesi

8

Uygulamaya bağlıdır:

  • MRG'de yok, YARV daha yakın.
  • JRuby ve MacRuby var.




Yakut sahiptir kilitler olarak Blocks, lambdasve Procs. JRuby'deki kapaklardan ve çoklu çekirdeklerden tam olarak yararlanmak için Java'nın uygulayıcıları işe yarar ; MacRuby için GCD kuyruklarını seviyorum .

Not ki yaratmak için güçlü olmak gerçek "OS düzeyinde" konuları paralel işlem için birden cpu çekirdek kullanabilirsiniz anlamına gelmez. Aşağıdaki örneklere bakın.

Bu, Ruby 2.1.0 kullanarak 3 iş parçacığı kullanan basit bir Ruby programının çıktısıdır :

(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 

Burada görebileceğiniz gibi, dört işletim sistemi iş parçacığı vardır, ancak yalnızca durumu olan iş parçacığı Rçalışır. Bunun nedeni Ruby'nin iş parçacıklarının nasıl uygulandığına dair bir sınırlamadır.



Aynı program, şimdi JRuby ile. Durumu olan üç iş parçacığı görebilirsiniz R, yani paralel olarak ilerliyorlar.

(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 


Aynı program, şimdi MacRuby ile. Paralel çalışan üç iplik de vardır. Bunun nedeni MacRuby iş parçacıklarının POSIX iş parçacıkları ( gerçek "İşletim Sistemi düzeyinde" iş parçacıkları ) olması ve GVL olmamasıdır

(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 


Bir kez daha, aynı program ama şimdi eski güzel MR ile. Bu uygulamanın yeşil-iş parçacıkları kullanması nedeniyle, yalnızca bir iş parçacığı görünür

(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 çoklu iş parçacığı ile ilgileniyorsanız, raporumu çatal işleyicileri kullanarak paralel programlarda hata ayıklama ilginç bulabilirsiniz.
Ruby içlerine daha genel bir bakış için Ruby Under a Microscope iyi bir okuma.
Ayrıca, Ruby Threads ve Omniref'deki C'deki Global Tercüman Kilidi , kaynak kodda Ruby iş parçacıklarının neden paralel çalışmadığını açıklar.


RMI ile MR mı demek istediniz?
Mayuresh Srivastava

4

Drb kullanmaya ne dersiniz ? Gerçek çoklu iş parçacığı değil, birkaç işlem arasındaki iletişimdir, ancak şimdi 1.8'de kullanabilirsiniz ve oldukça düşük sürtünme.


3

"Sistem Monitörü" nin bu soruyu cevaplamasına izin vereceğim. Her iki durumda da bir i7 (4 hiper iş parçacıklı çekirdek) makinede çalışan 8 Ruby iş parçacığı ile aynı kodu (asal sayıları hesaplar aşağıda) yürütüyorum ... ilk çalıştırma ile:

jruby 1.5.6 (yakut 1.8.7 patchlevel 249) (2014-02-03 6586) (OpenJDK 64-Bit Sunucu VM 1.7.0_75) [amd64-java]

İkincisi:

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

İlginç bir şekilde, CPU JRuby iş parçacıkları için daha yüksektir, ancak tamamlanma süresi yorumlanan Ruby için biraz daha kısadır. Grafikten söylemek biraz zor, ancak ikinci (yorumlanmış Ruby) çalıştırma CPU'ların yaklaşık 1 / 2'sini kullanıyor (hiper iş parçacığı yok mu?)

resim açıklamasını buraya girin

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 kullanıyorsanız, iş parçacığı kodunu bir uzantı olarak veya ruby-inline taş kullanarak C olarak yazabilirsiniz.


1

Ruby'de Üretim seviyesi sistemi (beta kullanamayacağınız) için gerçekten paralelliğe ihtiyacınız varsa, süreçler muhtemelen daha iyi bir alternatiftir.
Ancak, kesinlikle JRuby altındaki konuları denemeye değer.

Ayrıca Ruby altında iş parçacığının geleceği ile ilgileniyorsanız, bu makaleyi yararlı bulabilirsiniz .


JRuby iyi bir seçenektir. İşlemleri kullanarak paralel işleme için github.com/grosser/parallel seviyorum Parallel.map(['a','b','c'], :in_processes=>3){...
user454322


1

Bu yanıtı düzenleyemediği için buraya yeni bir yanıt ekleyin.

Güncelleme (2017/05/08)

Bu makale çok eski ve bilgiler güncel değil (2017) izini takip etmiyor, Aşağıda bazı ekler var:

  1. Opal Ruby'den JavaScript'e kaynaktan kaynağa derleyicidir. Aynı zamanda Ruby corelib'in bir uygulaması var, Çok aktif bir geliştiricidir ve üzerinde çalışılan (ön uç) bir çok çerçeve var. ve üretime hazır. Javascript tabanlı olduğundan, paralel iş parçacığı desteklemiyor.

  2. truffleruby Ruby programlama dilinin yüksek performanslı bir uygulamasıdır. Oracle Labs tarafından GraalVM üzerine inşa edilen TruffleRuby, JRuby'nin bir çatalı olup, Rubinius projesinin koduyla birleştirir ve aynı zamanda Ruby, MRI, hala canlı geliştirme, üretime hazır değil, standart uygulamadan kod içerir. Bu sürüm ruby ​​performans için doğmuş gibi görünüyor, paralel konuları destekleyip desteklemediğini bilmiyorum, ama sanırım öyle olmalı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.