Ok, il semble qu'il ne soit pas possible d'optimiser au sein de l'API publiée.
Code d'essai:
#extconf.rb
require 'mkmf'
dir_config("hello")
create_makefile("hello")
// hello.c
#include "ruby.h"
static VALUE rb_mHello;
static VALUE rb_cMyCalc;
static void calc_mark(void *f) { }
static void calc_free(void *f) { }
static VALUE calc_alloc(VALUE klass) { return Data_Wrap_Struct(klass, calc_mark, calc_free, NULL); }
static VALUE calc_init(VALUE obj) { return Qnil; }
static VALUE calc_merge(VALUE obj, VALUE h1, VALUE h2) {
return rb_funcall(h1, rb_intern("merge"), 1, h2);
}
static VALUE
calc_merge2(VALUE obj, VALUE h1, VALUE h2)
{
VALUE h3 = rb_hash_new();
VALUE keys;
VALUE akey;
keys = rb_funcall(h1, rb_intern("keys"), 0);
while (akey = rb_each(keys)) {
rb_hash_aset(h3, akey, rb_hash_aref(h1, akey));
}
keys = rb_funcall(h2, rb_intern("keys"), 0);
while (akey = rb_each(keys)) {
rb_hash_aset(h3, akey, rb_hash_aref(h2, akey));
}
return h3;
}
static VALUE
calc_merge3(VALUE obj, VALUE h1, VALUE h2)
{
VALUE keys;
VALUE akey;
keys = rb_funcall(h1, rb_intern("keys"), 0);
while (akey = rb_each(keys)) {
rb_hash_aset(h2, akey, rb_hash_aref(h1, akey));
}
return h2;
}
void
Init_hello()
{
rb_mHello = rb_define_module("Hello");
rb_cMyCalc = rb_define_class_under(rb_mHello, "Calculator", rb_cObject);
rb_define_alloc_func(rb_cMyCalc, calc_alloc);
rb_define_method(rb_cMyCalc, "initialize", calc_init, 0);
rb_define_method(rb_cMyCalc, "merge", calc_merge, 2);
rb_define_method(rb_cMyCalc, "merge2", calc_merge, 2);
rb_define_method(rb_cMyCalc, "merge3", calc_merge, 2);
}
# test.rb
require "hello"
h1 = Hash.new()
h2 = Hash.new()
1.upto(100000) { |x| h1[x] = x+1; }
1.upto(100000) { |x| h2["#{x}-12"] = x+1; }
c = Hello::Calculator.new()
puts c.merge(h1, h2).keys.length if ARGV[0] == "1"
puts c.merge2(h1, h2).keys.length if ARGV[0] == "2"
puts c.merge3(h1, h2).keys.length if ARGV[0] == "3"
Maintenant, les résultats des tests:
$ time ruby test.rb
real 0m1.021s
user 0m0.940s
sys 0m0.080s
$ time ruby test.rb 1
200000
real 0m1.224s
user 0m1.148s
sys 0m0.076s
$ time ruby test.rb 2
200000
real 0m1.219s
user 0m1.132s
sys 0m0.084s
$ time ruby test.rb 3
200000
real 0m1.220s
user 0m1.128s
sys 0m0.092s
Il semble donc que nous pourrions raser au maximum de ~ 0.004s sur une opération 0,2s. Etant donné qu'il n'y a probablement pas grand-chose d'autre à part définir les valeurs, il n'y a peut-être pas beaucoup d'espace pour d'autres optimisations. Peut-être essayer de pirater la source de rubis elle-même - mais à ce stade, vous ne développez plus vraiment "extension" mais plutôt changer le langage, donc ça ne marchera probablement pas. Si la jointure des hachages est quelque chose que vous devez faire plusieurs fois dans la partie C - alors probablement utiliser les structures de données internes et les exporter seulement dans le hachage Ruby dans le dernier passage serait le seul moyen d'optimiser les choses.
p.s. Le squelette initial pour le code emprunté de this excellent tutorial
"la performance est très critique dans cette application." == pas Ruby, à mon humble avis. Ce que vous mentionnez ressemble à la seule approche sans construire dans le code fragile. –
Cela ne me dérange pas si le code est fragile, juste tant qu'il fonctionne sur ruby 1.8.6 et ruby 1.9.1 (même avec l'aide de préprocesseur defs) – horseyguy
J'ai fait quelques tests, et on dirait que vous ne serez pas être en mesure d'obtenir beaucoup en utilisant l'API standard ... Je vais poster le code dans une réponse juste pour le souci de mise en forme. –