sumbur 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ if RUBY_ENGINE == 'ruby'
2
+ require 'mkmf'
3
+ create_makefile("native_sumbur")
4
+ else
5
+ File.open(File.dirname(__FILE__) + "/Makefile", 'w') do |f|
6
+ f.write("install:\n\t#nothing to build")
7
+ end
8
+ end
@@ -0,0 +1,68 @@
1
+ #include "ruby.h"
2
+
3
+ #define L 0xFFFFFFFF
4
+ static VALUE
5
+ rb_sumbur(VALUE self, VALUE hashed_int, VALUE capacity)
6
+ {
7
+ unsigned int h = NUM2UINT(hashed_int);
8
+ unsigned int capa = NUM2UINT(capacity);
9
+ unsigned int part, n, i, c;
10
+
11
+ if (capacity == 0) {
12
+ rb_raise(rb_eArgError, "Sumbur is not applicable to empty cluster");
13
+ }
14
+
15
+ part = L / capa;
16
+
17
+ if (L - h <= part) return INT2FIX(0);
18
+
19
+ n = 1;
20
+
21
+ do {
22
+ if (h >= L / 2) h -= L / 2;
23
+ else {
24
+ n = 2;
25
+ if (L / 2 - h < part) break;
26
+ }
27
+ if (capa == 2) break;
28
+
29
+ #define curslice(i) (L / (i * (i - 1)))
30
+ #define unroll(i) \
31
+ if (curslice(i) <= h) h -= curslice(i); \
32
+ else { \
33
+ h += curslice(i) * (i - n - 1); \
34
+ n = i; \
35
+ if (L / i - h < part) break; \
36
+ } \
37
+ if (capa == i) break
38
+
39
+ unroll(3); unroll(4); unroll(5); unroll(6); unroll(7);
40
+ unroll(8); unroll(9); unroll(10); unroll(11); unroll(12);
41
+ unroll(13); unroll(14); unroll(15); unroll(16); unroll(17);
42
+ unroll(18); unroll(19); unroll(20); unroll(21); unroll(22);
43
+ unroll(23); unroll(24);
44
+
45
+ for(i = 25; i <= capa; i++) {
46
+ c = L / (i * (i - 1));
47
+ if (c <= h) {
48
+ h -= c;
49
+ }
50
+ else {
51
+ h += c * (i - n - 1);
52
+ n = i;
53
+ if (L / i - h < part) break;
54
+ }
55
+ }
56
+ } while(0);
57
+ return INT2FIX(n - 1);
58
+ }
59
+
60
+ void
61
+ Init_native_sumbur()
62
+ {
63
+ VALUE mod_sumbur = rb_define_module("Sumbur");
64
+ VALUE mod_native = rb_define_module_under(mod_sumbur, "Native");
65
+
66
+ rb_define_method(mod_native, "sumbur", rb_sumbur, 2);
67
+ rb_extend_object(mod_native, mod_native);
68
+ }
@@ -0,0 +1,31 @@
1
+ module Sumbur
2
+ module PureRuby
3
+ extend self
4
+
5
+ def sumbur(hashed_integer, cluster_capacity)
6
+ raise ArgumentError, "Sumbur is not applicable to empty cluster" if cluster_capacity == 0
7
+ raise ArgumentError, "Sumbur accepts only positive 32bit integers" if hashed_integer < 0 || hashed_integer > 0xFFFFFFFF
8
+
9
+ l = 0xFFFFFFFF
10
+ part = l / cluster_capacity
11
+
12
+ return 0 if l - hashed_integer < part
13
+
14
+ h = hashed_integer
15
+ n = 1
16
+ i = 2
17
+ while i <= cluster_capacity
18
+ c = l / (i * (i-1))
19
+ if c <= h
20
+ h -= c
21
+ else
22
+ h += c * (i-n-1)
23
+ n = i
24
+ break if l / n - h < part
25
+ end
26
+ i += 1
27
+ end
28
+ n - 1
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Sumbur
2
+ VERSION = "0.0.1"
3
+ end
data/lib/sumbur.rb ADDED
@@ -0,0 +1,13 @@
1
+ require "sumbur/version"
2
+
3
+ module Sumbur
4
+ begin
5
+ require 'sumbur/native_sumbur'
6
+ include Native
7
+ extend Native
8
+ rescue LoadError
9
+ require 'sumbur/pure_ruby'
10
+ include PureRuby
11
+ extend PureRuby
12
+ end
13
+ end
@@ -0,0 +1,67 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+
4
+ $spread_cache = {}
5
+
6
+ HASHED = (1..1_000_000).map{|i| [i.hash & 0xFFFFFFFF, i]}
7
+
8
+ def spread(num, capa, sumbur)
9
+ start = Time.now
10
+ v = $spread_cache[ [num, capa, sumbur] ] ||= HASHED[0, num].
11
+ map{|hash, int| [sumbur.sumbur(hash, capa), int]}.
12
+ group_by{|serv, int| serv}
13
+ d = Time.now - start
14
+ puts format("%s %.4f", [num, capa, sumbur].inspect, d) if d > 0.01
15
+ v
16
+ end
17
+
18
+ shared_example = proc do
19
+ it "should spread capacity" do
20
+ for num in [100_000, 1_000_000]
21
+ for capa in [2,3,7,8,17,18]
22
+ spread(num, capa, sumbur).each{|serv, ints|
23
+ ints.size.must_be_within_epsilon num/capa, 7000.0/num
24
+ }
25
+ end
26
+ end
27
+ end
28
+
29
+ it "should reshard values cleanly" do
30
+ for num in [100_000, 1_000_000]
31
+ for capa in [2,3,7,8,17,18]
32
+ cur = spread(num, capa, sumbur)
33
+ nxt = spread(num, capa+1, sumbur)
34
+ moved = 0
35
+ for i in 0...capa
36
+ (nxt[i] - cur[i]).must_be_empty
37
+ moved += mvd = (cur[i] - nxt[i]).size
38
+ mvd.must_be_within_epsilon (num/capa - num/(capa+1)), 40000.0/num
39
+ end
40
+ moved.must_be_within_epsilon num/(capa+1), 7000.0/num
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ require 'sumbur/pure_ruby'
47
+ describe "Pure Ruby" do
48
+ let(:sumbur){ Sumbur::PureRuby }
49
+ class_exec &shared_example
50
+ end
51
+
52
+ begin
53
+ require 'sumbur/native_sumbur'
54
+
55
+ describe "Native" do
56
+ let(:sumbur) { Sumbur::Native }
57
+ class_exec &shared_example
58
+
59
+ it "should produce same spread as pure ruby version" do
60
+ for capa in [2,3,4,7,8,9,17,18,19]
61
+ spread(1_000_000, capa, Sumbur::Native).must_equal spread(1_000_000, capa, Sumbur::PureRuby)
62
+ end
63
+ end
64
+ end
65
+ rescue LoadError
66
+ puts "Native version is not tested"
67
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sumbur
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sokolov Yura 'funny-falcon'
9
+ - Maksim Kalinchenko
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-08-02 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: Sumbur - consistent spreading
16
+ email:
17
+ - funny.falcon@gmail.com
18
+ - uint32@mail.ru
19
+ executables: []
20
+ extensions:
21
+ - ext/sumbur/extconf.rb
22
+ extra_rdoc_files: []
23
+ files:
24
+ - ext/sumbur/extconf.rb
25
+ - ext/sumbur/sumbur.c
26
+ - lib/sumbur/pure_ruby.rb
27
+ - lib/sumbur/version.rb
28
+ - lib/sumbur.rb
29
+ - test/test_sumbur.rb
30
+ homepage: https://github.com/mailru/sumbur-ruby
31
+ licenses: []
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ - ext
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 1.8.24
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: Sumbur - consistent spreading
55
+ test_files:
56
+ - test/test_sumbur.rb