web-dev-qa-db-fra.com

Comment compiler Ruby?

Existe-t-il un outil qui me permette de compiler du code Ruby pour qu'il soit un peu plus rapide? 

Par exemple, j'ai entendu dire qu'il existe un outil pour Python appelé "pyc" qui nous permet de compiler le code afin qu'il s'exécute 10 fois plus vite.

28
Flethuseo

La réponse simple est que vous ne pouvez pas, du moins avec l’IRM 1.8 (standard). En effet, 1.8 fonctionne en parcourant l’arbre de syntaxe abstraite. Python, Ruby 1.9, JRuby et Rubinius utilisent un code octet, qui permet la compilation dans une représentation intermédiaire (code octet). Depuis MRI Ruby 2.3, il est devenu facile de le faire, voir cette réponse ci-dessous.

Avec Rubinius, vous pouvez faire quelque chose comme décrit dans cet article: http://rubini.us/2011/03/17/running-Ruby-with-no-Ruby/

Dans JRuby, vous pouvez utiliser le compilateur "Ahead Of Time" via, je crois, jrubyc.

Ce n'est pas vraiment la façon habituelle de faire les choses et il est généralement préférable de laisser votre implémentation Ruby la gérer comme elle le souhaite. Rubinius, au moins, mettra en cache le code octet après la première compilation, en le mettant à jour selon les besoins.

24
Ben Hughes

Début 2013, il n'existe aucun moyen de traduire Ruby en source C/C++, puis de le compiler.

Cependant, j'ai entendu Matz (Yukihiro Matsumoto) dire qu'un chercheur est en train de créer cet outil au Japon. Le projet devrait être fondé par le gouvernement japonais.

Sinon, vous pouvez utiliser JRuby et le compiler en code octet Java, ou Rubinius. Rubinius se compile automatiquement en octet-code (compilateur JIT) pour la machine virtuelle Rubinius. Il est possible de convertir Rubinius en code octet en LLVM IR et que LLVM peut générer du code machine.

6
rtacconi

Vérifiez le Unholy git repo

4
Pablo Fernandez

Depuis Ruby 2.3.0, il est si facile de compiler votre code source en bytecodes que le Ruby-VM comprend.

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

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

et en ligne de commande

$ 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

Le contenu du fichier

class A
  def shiva
    puts 'hello'
  end
end

Quel est le but?

Bien, Ruby a besoin de temps pour compiler votre code source en codes octets afin que vous puissiez charger vos codes octets directement dans Ruby et les exécuter. Pas de surcharge de vérification et de compilation de la grammaire. C'est beaucoup plus rapide que les processus normaux.

Comment charger le code octet?

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

instruction_from_byte_code.eval
# => :shiva

Note : Cette réponse est testée dansMRIuniquement. Cela pourrait ou non fonctionner dans d'autres implémentations de Ruby

4
illusionist

Je sais que c’est une vieille question mais j’ai trouvé un projet très intéressant qui pourrait apporter une réponse à votre question: http://crystal-lang.org/

Il compile essentiellement Ruby en code machine natif. Ce n'est pas tout à fait vrai, car Crystal n'est pas exactement Ruby et vous devrez peut-être apporter des modifications à votre code. Il y a aussi des bibliothèques qui ne sont pas encore supportées, mais pour moi tout semble très prometteur.

3
DP.

Le test suivant Ruby "autonome" est basé sur les exemples de ce fil même, tiré du commentaire/réponse de l'utilisateur nommé illusionist.

#!/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