window.pipedriveLeadboosterConfig={です。 ベース:'leadbooster-chat.pipedrive.com'、 companyId:11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2、 } ;(function () { var w = window もし (w.LeadBooster) {なら console.warn('LeadBooster already exists') } else { w.LeadBooster = { {. q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: 関数 (n) { { this.q.push({ t: 'o', n: n, h: h }) this.q.push({ t: 't', n: n }) }, } } })() rubyのフォークとスレッド - The Codest
The Codest
  • 会社概要
  • サービス
    • ソフトウェア開発
      • フロントエンド開発
      • バックエンド開発
    • Staff Augmentation
      • フロントエンド開発者
      • バックエンド開発者
      • データエンジニア
      • クラウドエンジニア
      • QAエンジニア
      • その他
    • アドバイザリー
      • 監査&コンサルティング
  • 産業
    • フィンテック&バンキング
    • E-commerce
    • アドテック
    • ヘルステック
    • 製造業
    • 物流
    • 自動車
    • アイオーティー
  • 価値
    • CEO
    • CTO
    • デリバリー・マネージャー
  • チーム
  • Case Studies
  • ノウハウ
    • ブログ
    • ミートアップ
    • ウェビナー
    • リソース
採用情報 連絡先
  • 会社概要
  • サービス
    • ソフトウェア開発
      • フロントエンド開発
      • バックエンド開発
    • Staff Augmentation
      • フロントエンド開発者
      • バックエンド開発者
      • データエンジニア
      • クラウドエンジニア
      • QAエンジニア
      • その他
    • アドバイザリー
      • 監査&コンサルティング
  • 価値
    • CEO
    • CTO
    • デリバリー・マネージャー
  • チーム
  • Case Studies
  • ノウハウ
    • ブログ
    • ミートアップ
    • ウェビナー
    • リソース
採用情報 連絡先
戻る矢印 戻る
2016-10-06
ソフトウェア開発

rubyのフォークとスレッド化

マレク・ギェルラッハ

ご存知のように、Ruby には MRI、JRuby、Rubinius、Opal、RubyMotion などの実装があり、それぞれ異なるコード実行パターンを使っています。この記事では、そのうちの最初の 3 つに焦点を当て、MRI

ご存知のように、RubyにはMRI、JRuby、Rubinius、Opal、RubyMotionなどいくつかの実装があり、それぞれ異なるパターンの コード を実行する。この記事では、そのうちの最初の3つに焦点を当て、MRI(現在最も人気のある実装)とJRuby、Rubiniusを、CPU負荷の高いアルゴリズムの処理やファイルのコピーなど、様々な状況におけるフォークとスレッドの適合性を評価するためのいくつかのサンプルスクリプトを実行することで比較する。

フォーク

  • は新しい子プロセス(親プロセスのコピー)である。
  • は新しいプロセス識別子(PID)
  • メモリは別*
  • は、メッセージキュー、ファイル、ソケットなどのプロセス間通信(IPC)チャネルを介して他と通信する。
  • 親プロセスが終了しても存在する
  • はPOSIXコールで、主にUnixプラットフォームで動作します。

スレッド

  • は実行コンテキスト「だけ」であり、プロセス内で働く
  • すべてのメモリを他と共有する(デフォルトではフォークよりも少ないメモリしか使わない)
  • 共有メモリオブジェクトによって他と通信する
  • プロセスが死ぬ
  • は、スターベーションやデッドロックなどの典型的なマルチスレッド問題を引き起こす。

例えば、アプリケーション・サーバー・レベルではUnicorn(フォーク)とPuma(スレッド)、バックグラウンド・ジョブ・レベルではResque(フォーク)とSidekiq(スレッド)などだ。

以下の表は、主要なRuby実装におけるフォークとスレッドのサポートを示している。

Rubyの実装フォークスレッディング
MRIはいあり(GIL**による制限あり)
JRuby–はい
ルビニウスはいはい

このトピックでは、さらに2つのマジックワードがブーメランのように戻ってくる。まず第一に、これらの言葉を同じ意味で使うことはできない。一言でいえば、2つ以上のタスクがまったく同時に処理されることを並列性という。同時並行性は、2つ以上のタスクが(必ずしも同時とは限らないが)重なった時間帯に処理される場合に起こる。大雑把な説明になってしまったが、この違いに気づき、この記事の続きを理解するのに十分だろう。

2020年に向けて

以下の表は、並列性と並行性のサポートを示している。

Rubyの実装並列性(フォーク経由)並列処理(スレッド経由)コンカレンシー
MRIはいいいえはい
JRuby–はいはい
ルビニウスはいはい(バージョン2.X以降)はい

理論的な話はここまでにして、実際にやってみよう!

  • メモリを分けても、親プロセスと同じ量のメモリを消費する必要はない。メモリ最適化のテクニックはいくつかある。そのひとつがコピー・オン・ライト(CoW)で、親プロセスは割り当てられたメモリをコピーせずに子プロセスと共有することができる。CoWでは、子プロセスが共有メモリを変更する場合にのみ、追加のメモリが必要となる。Rubyの場合、すべての実装がCoWに対応しているわけではない。たとえば、MRIはバージョン2.XからCoWを完全にサポートしている。
  • MRIの最大の長所/短所(不適切な選択肢は除外する)の1つは、GIL(グローバル・インタープリタ・ロック)の使用である。一言で言えば、このメカニズムはスレッドの実行を同期させる役割を担っており、一度に実行できるスレッドは1つだけということになる。でもちょっと待って、MRIでスレッドを使う意味がまったくないということなのだろうか?その答えは、GILの内部を理解すること......あるいは、少なくともこの記事のコード・サンプルを見てみることでわかる。

テストケース

Rubyの実装でフォークとスレッドがどのように機能するかを説明するために、次のような単純なクラスを作成した。 テスト と、それを継承するいくつかのクラスがある。それぞれのクラスが処理するタスクは異なる。デフォルトでは、すべてのタスクはループで4回実行される。また、すべてのタスクは、シーケンシャル、フォーク、スレッドの3種類のコード実行に対して実行される。さらに ベンチマーク.bmbm 一度目は実行環境を立ち上げて実行し、二度目は計測するためである。この記事で紹介する結果はすべて2回目の実行で得られたものである。もちろん bmbm メソッドは完全な分離を保証するものではないが、複数のコードを実行した場合の違いは些細なものだ。

require "benchmark"

クラス テスト
  AMOUNT = 4

  def run
    ベンチマーク.bmbm do |b|
      b.report("sequential") { sequential }.
      b.report("forking") { フォーキング } b.report("threading") { スレッディング
      b.report("threading") { スレッディング }.
    終了
  終了

  プライベート

  def sequential
    AMOUNT.times { 実行 }.
  終了

  def forking
    AMOUNT.times do
      フォークする
        実行
      終了
    終了

    プロセス.waitall
  rescue NotImplementedError => e
    # forkメソッドはJRubyでは利用できない
    e を置く
  終了

  def threading
    スレッド = []

    AMOUNT.times do
      スレッド << Thread.new do
        実行
      終了
    終了

    スレッド.map(&:join)
  終了

  def perform
    raise "実装されていません"
  end
終了
負荷テスト

ループで計算を実行し、大きなCPU負荷を発生させる。

class LoadTest < テスト
  def 実行
    1000.times { 1000.times { 2**3**4 } }.}
  終了
終了

走らせてみよう...

LoadTest.new.run

...そして結果をチェックする

MRIJRubyルビニウス
シーケンシャル1.8629282.0890001.918873
分岐0.945018–1.178322
スレッディング1.9139821.1070001.213315

ご覧の通り、逐次実行の結果は似ている。もちろん、解答の間にはわずかな違いがあるが、それはさまざまなインタープリターで選択されたメソッドの根本的な実装に起因するものだ。

この例では、フォークすることでパフォーマンスが大幅に向上する(コードの実行が約2倍速くなる)。

スレッドを使用すると、forkと同様の結果が得られるが、JRubyとRubiniusの場合のみである。MRI上でスレッドを使ってサンプルを実行すると、シーケンシャルな方法よりも少し時間がかかる。少なくとも2つの理由がある。第一に、GILは逐次的なスレッド実行を強制するので、完璧な世界では実行時間は逐次的な実行と同じになるはずだが、GIL操作(スレッド間の切り替えなど)のための時間のロスも発生する。第二に、スレッドを作成するためのオーバーヘッド時間も必要である。

この例では、MRIにおけるスレッドの使用感についての質問に対する答えは得られない。別の例を見てみよう。

スヌーズテスト

スリープ・メソッドを実行する。

class SnoozeTest < テスト
  def perform
    スリープ 1
  終了
終了

以下はその結果である。

MRIJRubyルビニウス
シーケンシャル4.0046204.0060004.003186
分岐1.022066–1.028381
スレッディング1.0015481.0040001.003642

ご覧のように、各実装は逐次実行とフォーク実行だけでなく、スレッド実行でも同じような結果を出している。では、なぜMRIはJRubyやRubiniusと同じような性能向上を実現できたのだろうか?その答えは 睡眠.

MRI 睡眠 メソッドは rb_thread_wait_for という別の関数を使用する。 ネイティブスリープ.その実装を見てみよう(コードは簡略化されている。 これ):

static void
native_sleep(rb_thread_t *th, struct timeval *timeout_tv)
{
  ...

  gvl_unlock_begin();
  {
    // ここでいくつかのことを行う
  }
  gvl_unlock_end();

  thread_debug("native_sleep donen");

この関数がなぜ重要かというと、厳密なRubyのコンテキストを使うだけでなく、システムのコンテキストに切り替えてそこで何らかの処理を行うからだ。このような状況では、Rubyプロセスは何もすることがない。そうとも言えない:「このスレッドでやることがない?このスレッドでやることがないのなら、別のスレッドに切り替えて、しばらくしてからここに戻ってきましょう」というGILがあるからだ。これは、GILを gvl_unlock_begin() そして gvl_unlock_end() の機能がある。

状況は明らかになったが 睡眠 メソッドはほとんど役に立たない。もっと実例が必要だ。

ファイル・ダウンロード・テスト

ファイルをダウンロードして保存するプロセスを実行する。

require "net/http"

class DownloadFileTest < テスト
  def perform
    Net::HTTP.get("upload.wikimedia.org", "/wikipedia/commons/thumb/7/73/Ruby_logo.svg/2000px-Ruby_logo.svg.png")
  終了
終了

以下の結果についてコメントする必要はない。上の例とよく似ている。

1.003642JRubyルビニウス
シーケンシャル0.3279800.3340000.329353
分岐0.104766–0.121054
スレッディング0.0857890.0940000.088490

他の良い例としては、ファイルのコピー処理やその他のI/O操作がある。

結論

  • ルビニウス はフォークとスレッドの両方を完全にサポートしている(GILが削除されたバージョン2.X以降)。あなたのコードは同時並行で実行できる。
  • JRuby はスレッドでは良い仕事をしているが、フォークにはまったく対応していない。並列性と並行性はスレッドで実現できる。
  • MRI はフォークをサポートしているが、スレッド化はGILの存在によって制限されている。スレッドによる同時実行は可能だが、実行中のコードがRubyインタプリタ・コンテキストの外に出る場合(IO操作やカーネル関数など)に限られる。並列性を実現する方法はない。

関連記事

ソフトウェア開発

将来を見据えたウェブ・アプリケーションの構築:The Codestのエキスパート・チームによる洞察

The Codestが、最先端技術を駆使してスケーラブルでインタラクティブなウェブアプリケーションを作成し、あらゆるプラットフォームでシームレスなユーザー体験を提供することにどのように秀でているかをご覧ください。The Codestの専門知識がどのようにデジタルトランスフォーメーションとビジネス...

ザ・コデスト
ソフトウェア開発

ラトビアを拠点とするソフトウェア開発企業トップ10社

ラトビアのトップソフトウェア開発企業とその革新的なソリューションについて、最新記事でご紹介します。ラトビアの技術リーダーたちがあなたのビジネスをどのように向上させるかをご覧ください。

thecodest
エンタープライズ&スケールアップ・ソリューション

Javaソフトウェア開発の要点:アウトソーシングを成功させるためのガイド

outsourcingのJavaソフトウェア開発を成功させるために不可欠なこのガイドを読んで、The Codestで効率性を高め、専門知識にアクセスし、プロジェクトを成功に導きましょう。

thecodest
ソフトウェア開発

ポーランドにおけるアウトソーシングの究極ガイド

ポーランドのoutsourcingの急増は、経済、教育、技術の進歩がITの成長とビジネス・フレンドリーな環境を促進していることによる。

ザ・コデスト
エンタープライズ&スケールアップ・ソリューション

IT監査ツール&テクニック完全ガイド

IT監査は、安全かつ効率的で、コンプライアンスに準拠したシステムを保証します。その重要性については、記事全文をお読みください。

The Codest
ヤクブ・ヤクボヴィッチ CTO & 共同創設者

ナレッジベースを購読して、IT部門の専門知識を常に最新の状態に保ちましょう。

    会社概要

    The Codest - ポーランドに技術拠点を持つ国際的なソフトウェア開発会社。

    イギリス - 本社

    • オフィス 303B, 182-184 High Street North E6 2JA
      イギリス、ロンドン

    ポーランド - ローカル・テック・ハブ

    • ファブリチュナ・オフィスパーク、アレハ
      ポコジュ18、31-564クラクフ
    • ブレイン・エンバシー, コンストルクトースカ
      11, 02-673 Warsaw, Poland

      The Codest

    • ホーム
    • 会社概要
    • サービス
    • Case Studies
    • ノウハウ
    • 採用情報
    • 辞書

      サービス

    • アドバイザリー
    • ソフトウェア開発
    • バックエンド開発
    • フロントエンド開発
    • Staff Augmentation
    • バックエンド開発者
    • クラウドエンジニア
    • データエンジニア
    • その他
    • QAエンジニア

      リソース

    • 外部ソフトウェア開発パートナーとの協力に関する事実と神話
    • 米国から欧州へ:アメリカの新興企業がヨーロッパへの移転を決断する理由
    • テックオフショア開発ハブの比較:テックオフショア ヨーロッパ(ポーランド)、ASEAN(フィリピン)、ユーラシア(トルコ)
    • CTOとCIOの課題は?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • ウェブサイト利用規約

    著作権 © 2025 by The Codest。無断複写・転載を禁じます。

    jaJapanese
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek jaJapanese