Pregunta

Estoy escribiendo una extensión C para Ruby que realmente necesita para fusionar dos hashes, sin embargo la función rb_hash_merge () es estático en Ruby 1.8.6. He tratado en lugar de usar:

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

pero esto es demasiado lento, y el rendimiento es muy crítica en esta aplicación.

¿Alguien sabe cómo hacer para llevar a cabo esta fusión con eficacia y rapidez en cuenta?

(Nota He tratado simplemente mirando a la fuente de rb_hash_merge () y replicarlo pero está plagado de otras funciones estáticas, que son a su vez plagado de funciones aún más estáticas por lo que parece casi imposible separar lo que necesito ... de otra manera)

¿Fue útil?

Solución

Ok, parece que podría no ser posible optimizar dentro de la API publicada.

Código de ensayo:

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

Ahora los resultados de las pruebas:

$ 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

Así que parece que podría afeitarse con la máxima ~ 0.004s en una operación de 0,2 segundos.

Dado que probablemente no haya mucho además de establecer los valores, puede que no haya mucho espacio para otras optimizaciones. Tal vez intenta que cortar la fuente de rubí en sí -. Pero en ese momento ya no desarrolla realmente "extensión", sino más bien cambiar el idioma, por lo que probablemente no va a trabajar

Si la unión de los hashes es algo que tiene que ver muchas veces en la parte C -. Entonces probablemente utilizando las estructuras de datos internas y sólo exportarlos en Rubí de hash en la pasada final sería la única manera de optimizar las cosas

p.s. El esqueleto inicial para el código tomado de este excelente tutorial

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top