Question

Je suis en train d'écrire une extension C pour Ruby qui a vraiment besoin de fusionner deux hash, mais la fonction rb_hash_merge () est STATIC dans Ruby 1.8.6. J'ai essayé d'utiliser plutôt:

rb_funcall(hash1, rb_intern("merge"), 1, hash2);

mais cela est beaucoup trop lent, et la performance est très critique dans cette application.

Quelqu'un sait comment s'y prendre pour réaliser cette fusion avec efficacité et rapidité à l'esprit?

(Note Je l'ai essayé de simplement regarder la source de rb_hash_merge () et le reproduire, mais il est criblée avec d'autres fonctions statiques, qui sont eux-mêmes criblé de fonctions encore plus statiques il semble presque impossible de démêler ... j'ai besoin une autre façon)

Était-ce utile?

La solution

Ok, on dirait peut-être 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 du test:

$ 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.

Étant donné qu'il n'y a probablement pas beaucoup plus de définir les valeurs, il n'y aurait pas beaucoup d'espace pour d'autres optimisations. Peut-être essayer de pirater la source rubis lui-même -. mais à ce moment-là vous ne développez vraiment « extension » mais plutôt changer la langue, il ne fonctionnera probablement pas

Si la jointure de hash est quelque chose que vous devez faire plusieurs fois dans la partie C -. Puis en utilisant probablement les structures de données internes et ne les exporter en hachage Ruby dans la passe finale serait la seule façon d'optimiser les choses

p.s. Le squelette initial pour le code emprunté cet excellent tutoriel

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top