Skip to content

Commit 57f25d1

Browse files
committed
make C extension work on newest ruby
BDIGIT api is internal ruby C api, and was removed from public api after merge of Bignum and Fixnum into Integer. Now use public C ruby api. It is slower than previous BDIGIT api, but much faster than ruby version user system total real distance3_bdigit 0.198673 0.000000 0.198673 (0.198672) distance3_public 0.373779 0.000000 0.373779 (0.373777) distance3_ruby 1.824285 0.000000 1.824285 (1.824315)
1 parent b3a7aad commit 57f25d1

File tree

2 files changed

+54
-32
lines changed

2 files changed

+54
-32
lines changed

extconf.rb

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,16 @@
33
File.write "Makefile", dummy_makefile(?.).join
44

55
unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.3.8")
6-
if ruby_source_dir = if File.directory? "/ruby"
7-
"-I/ruby" # for Github Actions: docker (currently disabled) and benchmark
8-
elsif ENV["RBENV_ROOT"] && ENV["RBENV_VERSION"] && File.exist?(t = "#{ENV["RBENV_ROOT"]}/sources/#{ENV["RBENV_VERSION"]}/ruby-#{ENV["RBENV_VERSION"]}/bignum.c") # https://github.com/rbenv/rbenv/issues/1199
9-
"-I#{File.dirname t}"
10-
end
11-
append_cppflags ruby_source_dir
12-
append_cppflags "-DRUBY_EXPORT" unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.4")
13-
create_makefile "idhash"
14-
# Why this hack?
15-
# 1. Because I want to use Ruby and ./idhash.bundle for tests, not C.
16-
# 2. Because I don't want to bother users with two gems instead of one.
17-
File.write "Makefile", <<~HEREDOC + File.read("Makefile")
18-
.PHONY: test
19-
test: all
20-
\t$(RUBY) -r./lib/dhash-vips.rb ./lib/dhash-vips-post-install-test.rb
21-
HEREDOC
22-
end
6+
append_cppflags "-DRUBY_EXPORT" unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.4")
7+
create_makefile "idhash"
8+
# Why this hack?
9+
# 1. Because I want to use Ruby and ./idhash.bundle for tests, not C.
10+
# 2. Because I don't want to bother users with two gems instead of one.
11+
File.write "Makefile", <<~HEREDOC + File.read("Makefile")
12+
.PHONY: test
13+
test: all
14+
\t$(RUBY) -r./lib/dhash-vips.rb ./lib/dhash-vips-post-install-test.rb
15+
HEREDOC
2316
end
2417

2518
__END__

idhash.c

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,58 @@
1-
#include <bignum.c>
1+
#include <ruby.h>
22

3+
// extract bignum to array of unsigned ints
4+
static unsigned int * idhash_bignum_to_buf(VALUE a, size_t *num) {
5+
size_t word_numbits = sizeof(unsigned int) * CHAR_BIT;
6+
size_t nlz_bits = 0;
7+
*num = rb_absint_numwords(a, word_numbits, &nlz_bits);
8+
9+
if (*num == (size_t)-1) {
10+
rb_raise(rb_eRuntimeError, "Number too large to represent and overflow occured");
11+
}
12+
13+
unsigned int *buf = ALLOC_N(unsigned int, *num);
14+
15+
rb_integer_pack(a, buf, *num, sizeof(unsigned int), 0,
16+
INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER|
17+
INTEGER_PACK_2COMP);
18+
19+
return buf;
20+
}
21+
22+
// does ((a ^ b) & (a | b) >> 128)
323
static VALUE idhash_distance(VALUE self, VALUE a, VALUE b){
4-
BDIGIT* tempd;
5-
long i, an = BIGNUM_LEN(a), bn = BIGNUM_LEN(b), templ, acc = 0;
6-
BDIGIT* as = BDIGITS(a);
7-
BDIGIT* bs = BDIGITS(b);
8-
while (0 < an && as[an-1] == 0) an--; // for (i = an; --i;) printf("%u\n", as[i]);
9-
while (0 < bn && bs[bn-1] == 0) bn--; // for (i = bn; --i;) printf("%u\n", bs[i]);
10-
// printf("%lu %lu\n", an, bn);
24+
size_t an, bn;
25+
unsigned int *as = idhash_bignum_to_buf(a, &an);
26+
unsigned int *bs = idhash_bignum_to_buf(b, &bn);
27+
28+
while (an > 0 && as[an-1] == 0) an--;
29+
while (bn > 0 && bs[bn-1] == 0) bn--;
30+
1131
if (an < bn) {
32+
unsigned int *tempd; size_t templ;
1233
tempd = as; as = bs; bs = tempd;
1334
templ = an; an = bn; bn = templ;
1435
}
15-
for (i = an; i-- > 4;) {
16-
// printf("%ld : (%u | %u) & (%u ^ %u)\n", i, as[i], (i >= bn ? 0 : bs[i]), as[i-4], bs[i-4]);
17-
acc += __builtin_popcountl((as[i] | (i >= bn ? 0 : bs[i])) & (as[i-4] ^ bs[i-4]));
18-
// printf("%ld : %ld\n", i, acc);
36+
37+
size_t i;
38+
long acc = 0;
39+
// to count >> 128
40+
size_t cycles = 128 / (sizeof(unsigned int) * CHAR_BIT);
41+
42+
for (i = an; i-- > cycles;) {
43+
acc += __builtin_popcountl((as[i] | (i >= bn ? 0 : bs[i])) & (as[i-cycles] ^ (i-cycles >= bn ? 0 : bs[i-cycles])));
1944
}
45+
2046
RB_GC_GUARD(a);
2147
RB_GC_GUARD(b);
48+
xfree(as);
49+
xfree(bs);
50+
2251
return INT2FIX(acc);
2352
}
2453

2554
void Init_idhash() {
26-
VALUE m = rb_define_module("DHashVips");
27-
VALUE mm = rb_define_module_under(m, "IDHash");
28-
rb_define_module_function(mm, "distance3_c", idhash_distance, 2);
55+
VALUE m = rb_define_module("DHashVips");
56+
VALUE mm = rb_define_module_under(m, "IDHash");
57+
rb_define_module_function(mm, "distance3_c", idhash_distance, 2);
2958
}

0 commit comments

Comments
 (0)