sumbur 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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