Pergunta

Eu estou escrevendo uma extensão C para Ruby que realmente precisa mesclar dois hashes, no entanto a função rb_hash_merge () é estático em Ruby 1.8.6. Eu tentei em vez de usar:

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

mas isso é demasiado lento, eo desempenho é muito crítico nesta aplicação.

Alguém sabe como proceder para realizar esta fusão com eficiência e rapidez em mente?

(Nota Tentei simplesmente olhar para a fonte para rb_hash_merge () e replicá-lo, mas ele está repleta de outras funções estáticas, que são eles próprios repleta de funções ainda mais estáticas assim parece quase impossível separar ... preciso uma outra maneira)

Foi útil?

Solução

Ok, parece que pode não ser possível otimizar dentro da API publicada.

código de teste:

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

Agora os resultados do teste:

$ 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

Assim parece que pode raspar no máximo ~ 0.004s em uma operação de 0.2s.

Tendo em conta que provavelmente não há muito além de definir os valores, pode não haver muito espaço para novas otimizações. Talvez tentar cortar a própria fonte ruby ??-. Mas nesse ponto você já não realmente desenvolver "extensão", mas sim alterar o idioma, por isso provavelmente não vai funcionar

Se a junção de hash é algo que você precisa fazer muitas vezes na parte C -., Em seguida, provavelmente usando as estruturas de dados internos e apenas exportá-los para o Ruby hash no passe final seria a única maneira de otimizar as coisas

P.S. O esqueleto inicial para o código emprestado de este tutorial excelente

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top