atomic 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{atomic}
5
- s.version = "0.0.6"
5
+ s.version = "0.0.7"
6
6
  s.authors = ["Charles Oliver Nutter", "MenTaLguY"]
7
7
  s.date = Time.now.strftime('%Y-%m-%d')
8
8
  s.description = "An atomic reference implementation for JRuby and green or GIL-threaded impls"
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'optparse'
6
+ require 'thread'
7
+ require 'benchmark'
8
+
9
+ require 'atomic'
10
+
11
+ Thread.abort_on_exception = true
12
+
13
+ $conf = {
14
+ :lock => "atomic",
15
+ :num_threads => 100,
16
+ :count => 100_000,
17
+ :count_per_thread => nil,
18
+ :slow => nil,
19
+ }
20
+
21
+ OptionParser.new do |opts|
22
+ opts.on("-c", "--count NUM") do |n|
23
+ $conf[:count] = n.to_i
24
+ end
25
+ opts.on("-p", "--count-per-thread") do |n|
26
+ $conf[:count_per_thread] = n.to_i
27
+ end
28
+ opts.on("-t", "--num-threads NUM") do |n|
29
+ $conf[:num_threads] = n.to_i
30
+ end
31
+ opts.on("-s", "--slow NUM") do |n|
32
+ $conf[:slow] = n.to_i
33
+ end
34
+ opts.on("-l", "--lock atomic|mutex") do |x|
35
+ $conf[:lock] = x
36
+ end
37
+ opts.on("-h", "--help"){ puts opts; exit }
38
+ end.parse!(ARGV)
39
+
40
+ unless $conf[:count_per_thread]
41
+ $conf[:count_per_thread] = $conf[:count] / $conf[:num_threads]
42
+ end
43
+ $conf.delete(:count)
44
+
45
+ if $conf[:slow].to_i > 0
46
+ require 'digest/md5'
47
+ def slow_down
48
+ $conf[:slow].times do |i|
49
+ Digest::MD5.hexdigest(i.to_s)
50
+ end
51
+ end
52
+
53
+ ret = []
54
+ 10.times do
55
+ m = Benchmark.measure{ slow_down }
56
+ ret << m.real
57
+ end
58
+
59
+ $conf[:slow_time] = [ret.min, ret.max]
60
+ else
61
+ def slow_down; end
62
+ end
63
+
64
+ $stderr.puts $conf.inspect
65
+
66
+ def para_prepare(&block)
67
+ num_threads = $conf[:num_threads]
68
+ count = $conf[:count_per_thread]
69
+
70
+ if num_threads % 2 > 0
71
+ raise ArgumentError, "num_threads must be a multiple of two"
72
+ end
73
+
74
+ # Keep those threads together
75
+ tg = ThreadGroup.new
76
+
77
+ num_threads.times do |i|
78
+ diff = (i % 2 == 0) ? 1 : -1
79
+
80
+ t = Thread.new do
81
+ nil until $go
82
+ count.times do
83
+ yield diff
84
+ end
85
+ end
86
+
87
+ tg.add(t)
88
+ end
89
+
90
+ # Make sure all threads are started
91
+ while tg.list.find{|t| t.status != "run"}
92
+ Thread.pass
93
+ end
94
+
95
+ # For good measure
96
+ GC.start
97
+
98
+ $go = false
99
+
100
+ tg
101
+ end
102
+
103
+
104
+
105
+ $tg = nil
106
+ if $conf[:lock] == "atomic"
107
+ $atom = Atomic.new(0)
108
+ $tg = para_prepare do |diff|
109
+ $atom.update do |x|
110
+ slow_down
111
+ x + diff
112
+ end
113
+ end
114
+ else
115
+ $lock = Mutex.new
116
+ $value = 0
117
+ $tg = para_prepare do |diff|
118
+ $lock.synchronize do
119
+ slow_down
120
+ $value += diff
121
+ end
122
+ end
123
+ end
124
+
125
+
126
+ # Run !
127
+ #
128
+ # NOTE: It seems to me that this measurement method
129
+ # is sensible to how the system dispatches his resources.
130
+ #
131
+ # More precise caluclation could be done using
132
+ # getrusage's times
133
+ ret = Benchmark.measure do
134
+ $go = true
135
+ $tg.list.each{|t| t.join}
136
+ $go = false
137
+ end
138
+ puts ret.real
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
4
+ conf = {
5
+ :vary => "threads",
6
+ :lock => "atomic"
7
+ }
8
+
9
+ OptionParser.new do |opts|
10
+ opts.on("-l", "--lock atomic|mutex") do |l|
11
+ conf[:lock] = l
12
+ end
13
+ opts.on("-v", "--vary threads|speed") do |v|
14
+ conf[:vary] = v
15
+ end
16
+ opts.on("-h", "--help"){ puts opts; exit }
17
+ end.parse!(ARGV)
18
+
19
+ result = File.open("results_#{conf[:lock]}_#{conf[:vary]}.csv", "w")
20
+
21
+
22
+ if conf[:vary] == "threads"
23
+ # Vary the number of concurrent threads that update the value.
24
+ #
25
+ # There is a total count of 1mio updates that is distributed
26
+ # between the number of threads.
27
+ #
28
+ # A pair number of threads is used so that even add and odd substract 1.
29
+ # This avoid creating instances for Bignum since the number should
30
+ # stay in the Fixnum range.
31
+ #
32
+ (1..100).each do |i|
33
+ i = i * 2
34
+
35
+ ret = []
36
+ 10.times do
37
+ ret << `ruby ./bench_atomic_1.rb -l #{conf[:lock]} -t #{i}`.to_f
38
+ end
39
+
40
+ line = ([i] + ret).join(', ')
41
+
42
+ puts line
43
+ result.puts line
44
+ end
45
+ elsif conf[:vary] == "speed"
46
+ # Varies the execution time of the update block
47
+ # by using long calulation (MD5)
48
+ #
49
+ # NOTE: Thread.pass and sleep() are not usable by the atomic
50
+ # lock. It needs to run the whole block without hitting
51
+ # another atomic update otherwise it has to retry
52
+ #
53
+ # The expected result is that the atomic lock's performance
54
+ # will hit a certain threshold where it will be worse than mutexes.
55
+ #
56
+ (1..30).each do |i|
57
+
58
+ ret = []
59
+ 10.times do
60
+ ret << `ruby ./bench_atomic_1.rb -l #{conf[:lock]} -s #{i}`.to_f
61
+ end
62
+
63
+ line = ([i] + ret).join(', ')
64
+
65
+ puts line
66
+ result.puts line
67
+ end
68
+ end
@@ -1,7 +1,7 @@
1
1
  package org.jruby.ext.atomic;
2
2
 
3
3
  import java.io.IOException;
4
- import java.util.concurrent.atomic.AtomicReference;
4
+ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
5
5
  import org.jruby.Ruby;
6
6
  import org.jruby.RubyClass;
7
7
  import org.jruby.RubyModule;
@@ -36,46 +36,48 @@ public class AtomicReferenceLibrary implements Library {
36
36
 
37
37
  @JRubyClass(name="JRubyReference", parent="Object")
38
38
  public static class JRubyReference extends RubyObject {
39
- private final AtomicReference<IRubyObject> reference;
39
+ private volatile IRubyObject reference;
40
+ private final static AtomicReferenceFieldUpdater<JRubyReference, IRubyObject> UPDATER =
41
+ AtomicReferenceFieldUpdater.newUpdater(JRubyReference.class, IRubyObject.class, "reference");
40
42
 
41
43
  public JRubyReference(Ruby runtime, RubyClass klass) {
42
44
  super(runtime, klass);
43
- reference = new AtomicReference<IRubyObject>(runtime.getNil());
45
+ reference = runtime.getNil();
44
46
  }
45
47
 
46
48
  @JRubyMethod
47
49
  public IRubyObject initialize(ThreadContext context) {
48
50
  Ruby runtime = context.getRuntime();
49
- reference.set(runtime.getNil());
51
+ UPDATER.set(this, runtime.getNil());
50
52
  return runtime.getNil();
51
53
  }
52
54
 
53
55
  @JRubyMethod
54
56
  public IRubyObject initialize(ThreadContext context, IRubyObject value) {
55
57
  Ruby runtime = context.getRuntime();
56
- reference.set(value);
58
+ UPDATER.set(this, value);
57
59
  return runtime.getNil();
58
60
  }
59
61
 
60
62
  @JRubyMethod(name = {"get", "value"})
61
63
  public IRubyObject get() {
62
- return reference.get();
64
+ return UPDATER.get(this);
63
65
  }
64
66
 
65
67
  @JRubyMethod(name = {"set", "value="})
66
68
  public IRubyObject set(IRubyObject newValue) {
67
- reference.set(newValue);
69
+ UPDATER.set(this, newValue);
68
70
  return newValue;
69
71
  }
70
72
 
71
73
  @JRubyMethod
72
74
  public IRubyObject compare_and_set(ThreadContext context, IRubyObject oldValue, IRubyObject newValue) {
73
- return context.getRuntime().newBoolean(reference.compareAndSet(oldValue, newValue));
75
+ return context.getRuntime().newBoolean(UPDATER.compareAndSet(this, oldValue, newValue));
74
76
  }
75
77
 
76
78
  @JRubyMethod
77
79
  public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) {
78
- return reference.getAndSet(newValue);
80
+ return UPDATER.getAndSet(this, newValue);
79
81
  }
80
82
  }
81
83
  }
Binary file
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atomic
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 6
10
- version: 0.0.6
9
+ - 7
10
+ version: 0.0.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Charles Oliver Nutter
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-09-06 00:00:00 Z
19
+ date: 2011-11-28 00:00:00 Z
20
20
  dependencies: []
21
21
 
22
22
  description: An atomic reference implementation for JRuby and green or GIL-threaded impls
@@ -34,12 +34,13 @@ files:
34
34
  - lib/atomic_reference.jar
35
35
  - examples/atomic_example.rb
36
36
  - examples/bench_atomic.rb
37
+ - examples/bench_atomic_1.rb
38
+ - examples/graph_atomic_bench.rb
37
39
  - test/test_atomic.rb
38
40
  - ext/atomic_reference.c
39
41
  - ext/AtomicReferenceService.java
40
42
  - ext/extconf.rb
41
43
  - ext/org/jruby/ext/atomic/AtomicReferenceLibrary.java
42
- - README.txt
43
44
  - atomic.gemspec
44
45
  - Rakefile
45
46
  homepage: http://github.com/headius/ruby-atomic
data/README.txt DELETED
@@ -1,33 +0,0 @@
1
- atomic: An atomic reference implementation for JRuby and green or GIL-threaded
2
- Ruby implementations (MRI 1.8/1.9, Rubinius)
3
-
4
- == Summary ==
5
-
6
- This library provides:
7
-
8
- * an Atomic class that guarantees atomic updates to its contained value
9
-
10
- The Atomic class provides accessors for the contained "value" plus two update
11
- methods:
12
-
13
- * update will run the provided block, passing the current value and replacing
14
- it with the block result iff the value has not been changed in the mean time.
15
- It may run the block repeatedly if there are other concurrent updates in
16
- progress.
17
- * try_update will run the provided block, passing the current value and
18
- replacing it with the block result. If the value changes before the update
19
- can happen, it will throw Atomic::ConcurrentUpdateError.
20
-
21
- The atomic repository is at http://github.com/headius/ruby-atomic.
22
-
23
- == Usage ==
24
-
25
- require 'atomic'
26
-
27
- my_atomic = Atomic.new(0)
28
- my_atomic.update {|v| v + 1}
29
- begin
30
- my_atomic.try_update {|v| v + 1}
31
- rescue Atomic::ConcurrentUpdateError => cue
32
- # deal with it (retry, propagate, etc)
33
- end