web-dev-qa-db-ja.com

Rubyをコンパイルする方法は?

Rubyコードをコンパイルして、多少速く実行できるようにするツールはありますか?

たとえば、Python "pyc"というツールがあり、コードをコンパイルできるため、10倍高速に実行できると聞いています。

29
Flethuseo

簡単な答えは、少なくともMRI 1.8(標準)ではできないということです。これは、1.8が抽象構文ツリーを歩くことで機能するためです。 Python、Ruby 1.9、JRuby、およびRubiniusは、中間表現(バイトコード)へのコンパイルを可能にするバイトコードを使用します。MRIからRuby 2.3これは簡単です。下記の answer をご覧ください。

Rubiniusを使用すると、この投稿で説明されているように何かを行うことができます。 http://rubini.us/2011/03/17/running-Ruby-with-no-Ruby/

JRubyでは、jrubycを通じて「Ahead Of Time」コンパイラを使用できます。

これは実際には標準的な方法ではなく、通常はRuby実装に必要なように処理させるだけでよい。少なくとも、Rubiniusはバイトコードをキャッシュする最初のコンパイル、必要に応じて更新します。

25
Ben Hughes

2013年の初めには、RubyをC/C++ソースに変換してコンパイルする方法はありません。

しかし、Matz(松本幸宏)が研究者がこのツールを日本で作成していると言うのを聞いた。プロジェクトは日本政府によって設立されるべきです。

それ以外の場合は、JRubyを使用してJavaバイトコードでコンパイルするか、Rubiniusを使用できます。Rubiniusは、Rubinius VMのバイトコード(JITコンパイラー)で自動的にコンパイルします。バイトコードでLLVMにIRとLLVMはマシンコードを生成できます。

6
rtacconi

Ruby 2.3.0から、ソースコードをRuby-VMが理解できるバイトコードに簡単にコンパイルできます。

byte_code = RubyVM::InstructionSequence.compile_file '/home/john/somefile.rb'

File.binwrite '/home/john/bytecode', byte_code.to_binary

およびコマンドラインで

$ cat bytecode 

YARB�
IUsx86_64-linux*.*1

+1�!AA*1
!qy��������yyQ� E/home/john/somefile.rbE<main>E <class:A>EshivaEhelloEAEputsEcore#define_methodu����� 5M

ファイルの内容

class A
  def shiva
    puts 'hello'
  end
end

目的は何ですか?

まあ、Rubyはソースコードをバイトコードにコンパイルするのに時間がかかるので、バイトコードを直接Rubyにロードして実行できます。文法チェックとコンパイルのオーバーヘッドなし。通常のプロセスよりもはるかに高速です。

バイトコードを読み込む方法は?

bytecode = File.readbin('/home/john/bytecode')
instruction_from_byte_code = RubyVM::InstructionSequence.load_from_binary byte_code

instruction_from_byte_code.eval
# => :shiva

:この回答は[〜#〜] mri [〜#〜]のみでテストされています。他のRuby実装では動作する場合と動作しない場合があります

5
illusionist

nholy git repoを確認してください

4
Pablo Fernandez

私はこれが古い質問であることを知っていますが、あなたの質問への答えを提供するかもしれない非常に興味深いプロジェクトを見つけました: http://crystal-lang.org/

基本的にRubyをネイティブマシンコードにコンパイルします。Crystalは正確にRubyではなく、コードに変更を加える必要があるかもしれません。サポートされていない(まだ)ライブラリもありますが、私にはすべて非常に有望に見えます。

3
DP.

次の「自己完結型」Rubyテストケースは、イリュージョニストというユーザーのコメント/回答からのこのスレッドの例に基づいています。

#!/usr/bin/env Ruby
#==========================================================================
# This file is in public domain.
# The code of this file is based on the code fragments at the
# 2018_12_09 version of the:
#
#     https://stackoverflow.com/questions/5902334/how-to-compile-Ruby
#
# This file has been tested with the Ruby version
#
#     Ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
#
#-----start--of--the--boilerplate------------------------------------------

s_fp_home=ENV["HOME"].to_s
s_fp_tmp=s_fp_home+"/tmp" # using /tmp can be a security risk
s_fp_demofolder=s_fp_tmp+"/Ruby_bytecode_usage_demo_01"

def create_folder_if_needed(s_fp_in)
   if !Dir.exists? s_fp_in
      Dir.mkdir(s_fp_in)
      if !Dir.exists? s_fp_in
         raise(Exception.new("\n\n Folder creation failed.\n"+
         "GUID=='d6e409cb-e072-4441-9421-22630190c2e7'\n"))
      end # if
   end # if
end # create_folder_if_needed
create_folder_if_needed(s_fp_tmp)
create_folder_if_needed(s_fp_demofolder)

s_Rand=""
7.times{s_Rand<<("_"+Rand(100).to_s)}

s_fp_bytecode=s_fp_demofolder+"/awesome_bytecode"+s_Rand
s_fp_src=s_fp_demofolder+"/x"+s_Rand+".rb"

if File.exists? s_fp_src
   raise(Exception.new("\n\n This file should not exist yet.\n"+
   " s_fp_src=="+s_fp_src+"\n"+
   "GUID=='43ab3d45-1324-47af-9441-22630190c2e7'\n"))
end # if
IO.write(s_fp_src,"puts('');puts('Greetings from bytecode!');puts('')")
if !File.exists? s_fp_src
   raise(Exception.new("\n\n The file \n"+s_fp_src+"\n is missing.\n"+
   "GUID=='4aeb5e54-efe0-4111-a851-22630190c2e7'\n"))
end # if


#-----start--of--the--core--of--the--demo----------------------------------

bytecode_out = RubyVM::InstructionSequence.compile_file(s_fp_src)
IO.binwrite(s_fp_bytecode, bytecode_out.to_binary)

bytecode_in = IO.binread(s_fp_bytecode)
instruction_from_byte_code = RubyVM::InstructionSequence.load_from_binary(bytecode_in)
instruction_from_byte_code.eval

#==========================================================================
0
Martin Vahi