Frage

Ich schreibe eine C-Erweiterung für Ruby, die wirklich benötigt zwei Hashes zu fusionieren, aber die rb_hash_merge () Funktion in Ruby 1.8.6 STATIC ist. Ich habe stattdessen zu verwenden versucht:

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

, aber das ist viel zu langsam, und die Leistung ist sehr kritisch in dieser Anwendung.

Wer weiß, wie über die Durchführung diesen merge mit Effizienz und Geschwindigkeit im Auge gehen?

(Anmerkung Ich habe einfach versucht, an der Quelle für rb_hash_merge suchen () und es zu replizieren, aber es ist gespickt mit anderen statischen Funktionen, die sich mit noch mehr statischen Funktionen gespickt sind, so dass es fast unmöglich scheint, zu entwirren ... ich brauche eine andere Art und Weise)

War es hilfreich?

Lösung

Ok, sieht aus wie vielleicht nicht möglich sein, innerhalb der veröffentlichten API zu optimieren.

Testcode:

#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"

Jetzt sind die Testergebnisse:

$ 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

So sieht es aus, wie wir bei maximalen abrasieren könnten ~ 0.004s auf einem 0.2s Betrieb.

Da gibt es wahrscheinlich nicht viel außer Einstellung der Werte, kann es nicht so viel Platz sein für weitere Optimierungen. Vielleicht versuchen, die Ruby-Quelle zu hacken selbst. - aber an diesem Punkt nicht wirklich Sie mehr entwickeln „Erweiterung“, sondern die Sprache ändern, so wird es wahrscheinlich nicht funktionieren

Wenn die von Hashes verbinden ist etwas, das Sie brauchen, viele Male in dem C-Teil zu tun -. Dann wahrscheinlich die internen Datenstrukturen und nur sie in Ruby-Hash im letzten Durchgang Export wäre der einzige Weg, um Dinge zu optimieren

P. S. Das anfängliche Skelett für den Code entlehnt dieses hervorragenden Tutorial

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top