J'ai essayé de réimplémenter Ruby's Float (arg, exception: true) avec builtin

introduction

Cet article est un article qui redéfinit la méthode Float (arg, exception: true) en utilisant builtin introduit dans l'article suivant que j'ai écrit plus tôt.

J'ai implémenté Ruby avec Ruby (et C) (j'ai joué avec builtin)

Ce que vous faites est à peu près le même que dans l'article précédent. La différence par rapport à la dernière fois est que j'ai essayé de redéfinir la méthode qui peut être appelée n'importe où avec builtin au lieu de la méthode d'une classe spécifique.

Après avoir écrit l'article précédent, je me demandais si des méthodes telles que rise et Float pouvaient prendre en charge la fonction intégrée, alors j'ai eu envie de l'essayer.

De la conclusion, il semble que de telles méthodes globales puissent également être supportées par builtin (bien que cela puisse ne pas être recommandé car nous n'avons pas confirmé s'il s'agit du comportement prévu ...)

Qu'est-ce qui est intégré?

Builtin est d'implémenter Ruby lui-même dans Ruby (et C). Pour plus de détails, veuillez lire l'article précédent.

J'ai implémenté Ruby avec Ruby (et C) (j'ai joué avec builtin)

Environnement

J'utilise l'environnement de l'article précédent tel quel. Il n'y a pas de changements majeurs.

J'ai implémenté Ruby avec Ruby (et C) (j'ai joué avec builtin)

Je l'ai essayé

Trouvez l'emplacement d'implémentation de la méthode Float

Tout d'abord, recherchez la source qui implémente la méthode Float. Les méthodes Ruby sont généralement définies comme ceci.

rb_define_method(rb_mKernel, "to_s", rb_any_to_s, 0);

Le premier argument est la variable de classe / module dans laquelle la méthode est définie. Généralement, il est créé avec un nom de classe ou un nom de module tel que rb_cArray. Le deuxième argument, «to_s», est le nom de la méthode appelée côté Ruby. Le troisième argument est une fonction C qui est exécutée lorsque la méthode est appelée, et le dernier argument spécifie le nombre d'arguments que la méthode reçoit.

Cette fois, nous voulons réimplémenter la méthode Float, donc cherchons le code source Ruby avec git grep \" Float \ "ʻetc. Ensuite, il semble qu'il soit implémenté comme suit dans ʻobject.c.

rb_define_global_function("Float", rb_f_float, -1);

Réimplémentons-le.

Réimplémentation de la méthode Float

Dans l'article précédent, nous avons réimplémenté Hash # delete. Aussi, pour prendre en charge intégré, j'ai modifié common.mk etc. Cependant, dans le code maître Ruby actuel, Kernel # clone, qui a été implémenté dans ʻobject.c`, est rendu compatible avec builtin, il n'est donc pas nécessaire de les modifier.

Deux sources doivent être modifiées: ʻobject.c et kernel.rb`.

Modifier objet.c

Dans ʻobject.c, supprimez d'abord le code suivant qui définit la méthode Float`.

void
InitVM_Object(void)
{
    Init_class_hierarchy();

    //réduction
-    rb_define_global_function("Float", rb_f_float, -1);
    //réduction
}

Ensuite, modifiez le rb_f_float qui définit le traitement de la méthode Float comme suit.

- /*	
-  *  call-seq:	
-  *     Float(arg, exception: true)    -> float or nil	
-  *	
-  *  Returns <i>arg</i> converted to a float. Numeric types are	
-  *  converted directly, and with exception to String and	
-  *  <code>nil</code> the rest are converted using	
-  *  <i>arg</i><code>.to_f</code>.  Converting a String with invalid	
-  *  characters will result in a ArgumentError.  Converting	
-  *  <code>nil</code> generates a TypeError.  Exceptions can be	
-  *  suppressed by passing <code>exception: false</code>.	
-  *	
-  *     Float(1)                 #=> 1.0	
-  *     Float("123.456")         #=> 123.456	
-  *     Float("123.0_badstring") #=> ArgumentError: invalid value for Float(): "123.0_badstring"	
-  *     Float(nil)               #=> TypeError: can't convert nil into Float	
-  *     Float("123.0_badstring", exception: false)  #=> nil	
-  */	

static VALUE	static VALUE
- rb_f_float(int argc, VALUE *argv, VALUE obj)
+ rb_f_float(rb_execution_context_t *ec, VALUE main, VALUE arg, VALUE opts)
{
-    VALUE arg = Qnil, opts = Qnil;	
-
-    rb_scan_args(argc, argv, "1:", &arg, &opts);	
-    return rb_convert_to_float(arg, opts_exception_p(opts));	    
+   return rb_convert_to_float(arg, opts_exception_p(opts));
}

Puisqu'il utilise intégré et reçoit des arguments de mot-clé, il est défini sur VALUE main, VALUE arg, VALUE opts au lieu de ʻint argc, VALUE * argv`. De plus, «VALUE main» est ajouté pour prendre l'argument que la méthode globale reçoit implicitement à la place.

Ceci termine la modification dans ʻobject.c`!

Modifier kernel.rb

Dans Ruby, les méthodes globales qui peuvent être appelées de n'importe où peuvent être redéfinies dans le module Kernel.

Par exemple, la méthode «put» peut être modifiée comme suit:

module Kernel
   def puts *args
      p "hoge" 
   end
end


puts :ho
#=> "hoge"Est affiché

Utilisez ceci pour ajouter la méthode Float dans kernel.rb comme indiqué ci-dessous.

module Kernel
+ 
+   #
+   #  call-seq:
+   #     Float(arg, exception: true)    -> float or nil
+   #
+   #  Returns <i>arg</i> converted to a float. Numeric types are
+   #  converted directly, and with exception to String and
+   #  <code>nil</code> the rest are converted using
+   #  <i>arg</i><code>.to_f</code>.  Converting a String with invalid
+   #  characters will result in a ArgumentError.  Converting
+   #  <code>nil</code> generates a TypeError.  Exceptions can be
+   #  suppressed by passing <code>exception: false</code>.
+   #
+   #     Float(1)                 #=> 1.0
+   #     Float("123.456")         #=> 123.456
+   #     Float("123.0_badstring") #=> ArgumentError: invalid value for Float(): "123.0_badstring"
+   #     Float(nil)               #=> TypeError: can't convert nil into Float
+   #     Float("123.0_badstring", exception: false)  #=> nil
+   #
+   def Float(arg, exception: true)
+     __builtin_rb_f_float(arg, exception)
+   end
+ 
  #
  #  call-seq:
  #     obj.clone(freeze: nil) -> an_object
  #
  #  Produces a shallow copy of <i>obj</i>---the instance variables of
  #  <i>obj</i> are copied, but not the objects they reference.
  #  #clone copies the frozen value state of <i>obj</i>, unless the
  #  +:freeze+ keyword argument is given with a false or true value.
  #  See also the discussion under Object#dup.
  #
  #     class Klass
  #        attr_accessor :str
  #     end
  #     s1 = Klass.new      #=> #<Klass:0x401b3a38>
  #     s1.str = "Hello"    #=> "Hello"
  #     s2 = s1.clone       #=> #<Klass:0x401b3998 @str="Hello">
  #     s2.str[1,4] = "i"   #=> "i"
  #     s1.inspect          #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
  #     s2.inspect          #=> "#<Klass:0x401b3998 @str=\"Hi\">"
  #
  #  This method may have class-specific behavior.  If so, that
  #  behavior will be documented under the #+initialize_copy+ method of
  #  the class.
  #
  def clone(freeze: nil)
    __builtin_rb_obj_clone2(freeze)
  end
end

Le rb_f_float qui a été rendu compatible avec Builtin plus tôt est appelé avec __builtin_rb_f_float. Maintenant, la modification dans kernel.rb est OK.

finalement

Après cela, si vous pouvez construire en exécutant make et make install, l'implémentation avec builtin est terminée.

en conclusion

Il semble que des méthodes telles que Float et ʻInteger` peuvent être intégrées comme ceci. Indépendamment du fait qu'il ait été envoyé sous forme de patch, il était intéressant qu'il semble être tout à fait applicable.

En passant, lorsque j'ai essayé les benchmarks suivants, j'ai senti que je pouvais m'attendre à une amélioration des performances.

benchmark:
  float: "Float(42)"
  float_true: "Float(42, exception: true)"
  float_false: "Float(42, exception: false)"
loop_count: 10000

Résultats ci-dessous

Calculating -------------------------------------
                     compare-ruby  built-ruby
               float      37.495M     15.878M i/s -     10.000k times in 0.000267s 0.000630s
          float_true       1.109M      1.211M i/s -     10.000k times in 0.009015s 0.008261s
         float_false       1.227M      1.191M i/s -     10.000k times in 0.008150s 0.008395s

Comparison:
                            float
        compare-ruby:  37495314.0 i/s
          built-ruby:  15878056.0 i/s - 2.36x  slower

                       float_true
          built-ruby:   1210507.2 i/s
        compare-ruby:   1109250.0 i/s - 1.09x  slower

                      float_false
        compare-ruby:   1226948.7 i/s
          built-ruby:   1191128.5 i/s - 1.03x  slower

Si une méthode telle que Float est intégrée, je pense que la maintenabilité du code sera plus importante que les performances.

Recommended Posts

J'ai essayé de réimplémenter Ruby's Float (arg, exception: true) avec builtin
J'ai essayé DI avec Ruby
J'ai essayé de résoudre le problème de la "sélection multi-étapes" avec Ruby
J'ai essayé de démarrer avec Web Assembly
J'ai essayé de résoudre le problème de la séquence Tribonacci en Ruby, avec récurrence.
J'ai essayé de faire une authentification de base avec Java
J'ai essayé de gérer la configuration des jambes de force avec Coggle
J'ai essayé de gérer les informations de connexion avec JMX
J'ai essayé d'implémenter Ruby avec Ruby (et C) (j'ai joué avec intégré)
J'ai fait un blackjack avec Ruby (j'ai essayé d'utiliser minitest)
[Notions de base sur Ruby] J'ai essayé d'apprendre des modules (Chapitre 1)
J'ai essayé de casser le bloc avec java (1)
J'ai essayé ce que je voulais essayer avec Stream doucement.
J'ai essayé d'implémenter le téléchargement de fichiers avec Spring MVC
J'ai essayé d'implémenter TCP / IP + BIO avec JAVA
J'ai démarré MySQL 5.7 avec docker-compose et j'ai essayé de me connecter
J'ai essayé de démarrer avec Spring Data JPA
J'ai essayé de dessiner une animation avec l'API Blazor + canvas
J'ai essayé d'implémenter Sterling Sort avec Java Collector
J'ai essayé de créer un environnement de développement java8 avec Chocolatey
J'ai essayé de moderniser une application Java EE avec OpenShift.
J'ai essayé d'augmenter la vitesse de traitement avec l'ingénierie spirituelle
[Rails] J'ai essayé de créer une mini application avec FullCalendar
J'ai brièvement résumé la grammaire de base de Ruby
J'ai essayé de lier le chat avec le serveur de Minecraft avec l'API Discord
[Rails] J'ai essayé d'implémenter le traitement par lots avec la tâche Rake
Même en Java, je veux afficher true avec un == 1 && a == 2 && a == 3
J'ai essayé de créer un environnement de développement padrino avec Docker
J'ai essayé de démarrer avec Swagger en utilisant Spring Boot
J'ai essayé de pouvoir passer plusieurs objets avec Ractor
J'ai essayé UPSERT avec PostgreSQL.
J'ai essayé BIND avec Docker
J'ai essayé de vérifier yum-cron
Je souhaite ajouter une fonction de navigation avec ruby on rails
J'ai essayé de créer un environnement de serveur UML Plant avec Docker
J'ai essayé de me connecter à MySQL en utilisant le modèle JDBC avec Spring MVC
J'ai essayé d'écrire du code comme une déclaration de type en Ruby
[Rubiy] J'ai essayé de résumer le traitement de la boucle ce soir [fois, pause ...]
J'ai essayé d'implémenter la fonction de prévisualisation d'image avec Rails / jQuery
J'ai essayé de créer un environnement de développement http2 avec Eclipse + Tomcat
J'ai essayé d'implémenter un mappage OU flexible avec MyBatis Dynamic SQL
J'ai essayé de créer une application Android avec MVC maintenant (Java)
J'ai essayé de vérifier le fonctionnement du serveur gRPC avec grpcurl
J'ai essayé de faire un Numeron qui n'est pas bon avec Ruby
J'ai essayé de créer une fonction de groupe (babillard) avec Rails
J'ai essayé de mâcher C # (indexeur)
J'ai essayé d'utiliser JOOQ avec Gradle
J'ai essayé l'analyse morphologique avec MeCab