uringmachine 0.21.0 → 0.22.0

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/CHANGELOG.md +14 -0
  4. data/TODO.md +144 -0
  5. data/benchmark/README.md +173 -0
  6. data/benchmark/bm_io_pipe.rb +70 -0
  7. data/benchmark/bm_io_socketpair.rb +71 -0
  8. data/benchmark/bm_mutex_cpu.rb +57 -0
  9. data/benchmark/bm_mutex_io.rb +64 -0
  10. data/benchmark/bm_pg_client.rb +109 -0
  11. data/benchmark/bm_queue.rb +76 -0
  12. data/benchmark/chart.png +0 -0
  13. data/benchmark/common.rb +135 -0
  14. data/benchmark/dns_client.rb +47 -0
  15. data/{examples/bm_http_parse.rb → benchmark/http_parse.rb} +1 -1
  16. data/benchmark/run_bm.rb +8 -0
  17. data/benchmark/sqlite.rb +108 -0
  18. data/{examples/bm_write.rb → benchmark/write.rb} +4 -4
  19. data/ext/um/um.c +189 -100
  20. data/ext/um/um.h +36 -10
  21. data/ext/um/um_async_op.c +1 -1
  22. data/ext/um/um_class.c +87 -13
  23. data/ext/um/um_op.c +6 -0
  24. data/ext/um/um_sync.c +2 -2
  25. data/ext/um/um_utils.c +16 -0
  26. data/grant-2025/journal.md +118 -1
  27. data/grant-2025/tasks.md +48 -22
  28. data/lib/uringmachine/actor.rb +8 -0
  29. data/lib/uringmachine/dns_resolver.rb +1 -2
  30. data/lib/uringmachine/fiber_scheduler.rb +127 -81
  31. data/lib/uringmachine/version.rb +1 -1
  32. data/lib/uringmachine.rb +32 -3
  33. data/test/helper.rb +7 -18
  34. data/test/test_actor.rb +12 -3
  35. data/test/test_async_op.rb +10 -10
  36. data/test/test_fiber.rb +84 -1
  37. data/test/test_fiber_scheduler.rb +950 -47
  38. data/test/test_um.rb +297 -120
  39. data/uringmachine.gemspec +2 -1
  40. metadata +38 -16
  41. data/examples/bm_fileno.rb +0 -33
  42. data/examples/bm_queue.rb +0 -111
  43. data/examples/bm_side_running.rb +0 -83
  44. data/examples/bm_sqlite.rb +0 -89
  45. data/examples/dns_client.rb +0 -12
  46. /data/{examples/bm_mutex.rb → benchmark/mutex.rb} +0 -0
  47. /data/{examples/bm_mutex_single.rb → benchmark/mutex_single.rb} +0 -0
  48. /data/{examples/bm_send.rb → benchmark/send.rb} +0 -0
  49. /data/{examples/bm_snooze.rb → benchmark/snooze.rb} +0 -0
data/uringmachine.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.add_development_dependency 'rake-compiler', '~>1.3.0'
24
24
  s.add_development_dependency 'minitest', '~>5.26.2'
25
- s.add_development_dependency 'benchmark-ips', '~>2.14.0'
25
+ s.add_development_dependency 'benchmark'
26
+ s.add_development_dependency 'benchmark-ips'
26
27
  s.add_development_dependency 'http_parser.rb', '~>0.8.0'
27
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uringmachine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -37,20 +37,34 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: 5.26.2
40
+ - !ruby/object:Gem::Dependency
41
+ name: benchmark
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
40
54
  - !ruby/object:Gem::Dependency
41
55
  name: benchmark-ips
42
56
  requirement: !ruby/object:Gem::Requirement
43
57
  requirements:
44
- - - "~>"
58
+ - - ">="
45
59
  - !ruby/object:Gem::Version
46
- version: 2.14.0
60
+ version: '0'
47
61
  type: :development
48
62
  prerelease: false
49
63
  version_requirements: !ruby/object:Gem::Requirement
50
64
  requirements:
51
- - - "~>"
65
+ - - ">="
52
66
  - !ruby/object:Gem::Version
53
- version: 2.14.0
67
+ version: '0'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: http_parser.rb
56
70
  requirement: !ruby/object:Gem::Requirement
@@ -77,23 +91,31 @@ files:
77
91
  - ".github/workflows/test.yml"
78
92
  - ".gitignore"
79
93
  - ".gitmodules"
94
+ - ".rubocop.yml"
80
95
  - CHANGELOG.md
81
96
  - Gemfile
82
97
  - LICENSE
83
98
  - README.md
84
99
  - Rakefile
85
100
  - TODO.md
86
- - examples/bm_fileno.rb
87
- - examples/bm_http_parse.rb
88
- - examples/bm_mutex.rb
89
- - examples/bm_mutex_single.rb
90
- - examples/bm_queue.rb
91
- - examples/bm_send.rb
92
- - examples/bm_side_running.rb
93
- - examples/bm_snooze.rb
94
- - examples/bm_sqlite.rb
95
- - examples/bm_write.rb
96
- - examples/dns_client.rb
101
+ - benchmark/README.md
102
+ - benchmark/bm_io_pipe.rb
103
+ - benchmark/bm_io_socketpair.rb
104
+ - benchmark/bm_mutex_cpu.rb
105
+ - benchmark/bm_mutex_io.rb
106
+ - benchmark/bm_pg_client.rb
107
+ - benchmark/bm_queue.rb
108
+ - benchmark/chart.png
109
+ - benchmark/common.rb
110
+ - benchmark/dns_client.rb
111
+ - benchmark/http_parse.rb
112
+ - benchmark/mutex.rb
113
+ - benchmark/mutex_single.rb
114
+ - benchmark/run_bm.rb
115
+ - benchmark/send.rb
116
+ - benchmark/snooze.rb
117
+ - benchmark/sqlite.rb
118
+ - benchmark/write.rb
97
119
  - examples/echo_server.rb
98
120
  - examples/fiber_scheduler_demo.rb
99
121
  - examples/fiber_scheduler_fork.rb
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/inline'
4
-
5
- gemfile do
6
- source 'https://rubygems.org'
7
- gem 'benchmark'
8
- gem 'benchmark-ips'
9
- end
10
-
11
- require 'benchmark/ips'
12
-
13
- r, w = IO.pipe
14
-
15
- class IO
16
- def __fd__
17
- @__fd__ ||= fileno
18
- end
19
- end
20
-
21
- @map = ObjectSpace::WeakMap.new
22
-
23
- def cached_fileno(r)
24
- @map[r] ||= r.fileno
25
- end
26
-
27
- Benchmark.ips do |x|
28
- x.report('IO#fileno') { r.fileno }
29
- x.report('cached_fileno') { cached_fileno(r) }
30
- x.report('__fd__') { r.__fd__ }
31
-
32
- x.compare!(order: :baseline)
33
- end
data/examples/bm_queue.rb DELETED
@@ -1,111 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/inline'
4
-
5
- gemfile do
6
- source 'https://rubygems.org'
7
- gem 'uringmachine', path: '..'
8
- gem 'benchmark'
9
- gem 'benchmark-ips'
10
- end
11
-
12
- require 'benchmark/ips'
13
- require_relative '../lib/uringmachine'
14
-
15
- COUNT = 1000
16
- NUM_PRODUCERS = 8
17
- NUM_CONSUMERS = 8
18
-
19
- @queue = Queue.new
20
- @done = Queue.new
21
-
22
- def run_threads
23
-
24
- NUM_PRODUCERS.times do
25
- Thread.new do
26
- COUNT.times { @queue << rand(1000) }
27
- @done << true
28
- end
29
- end
30
-
31
- total = 0
32
- NUM_CONSUMERS.times do
33
- Thread.new do
34
- loop do
35
- item = @queue.shift
36
- break if item.nil?
37
-
38
- total += item
39
- end
40
- @done << true
41
- end
42
- end
43
-
44
- # wait for producers
45
- NUM_PRODUCERS.times { @done.shift }
46
-
47
- # stop and wait for consumers
48
- NUM_CONSUMERS.times do
49
- @queue << nil
50
- @done.shift
51
- end
52
-
53
- total
54
- end
55
-
56
- @machine = UM.new
57
- @um_queue = UM::Queue.new
58
- @um_done = UM::Queue.new
59
-
60
- def run_um
61
- NUM_PRODUCERS.times do
62
- @machine.spin do
63
- COUNT.times { @machine.push(@um_queue, rand(1000)) }
64
- @machine.push(@um_done, true)
65
- end
66
- end
67
-
68
- total = 0
69
- NUM_CONSUMERS.times do
70
- @machine.spin do
71
- loop do
72
- item = @machine.shift(@um_queue)
73
- break if item.nil?
74
-
75
- total += item
76
- end
77
- @machine.push(@um_done, true)
78
- end
79
- end
80
-
81
- # wait for producers
82
- NUM_PRODUCERS.times { @machine.shift(@um_done) }
83
-
84
- # stop and wait for consumers
85
- NUM_CONSUMERS.times do
86
- @machine.push(@um_queue, nil)
87
- @machine.shift(@um_done)
88
- end
89
-
90
- total
91
- end
92
-
93
- # puts "running"
94
- # res = run_threads
95
- # p threads: res
96
-
97
- # 100.times {
98
- # res = run_um
99
- # p fibers: res
100
- # }
101
-
102
- # __END__
103
-
104
- Benchmark.ips do |x|
105
- x.config(:time => 10, :warmup => 3)
106
-
107
- x.report("threads") { run_threads }
108
- x.report("UM") { run_um }
109
-
110
- x.compare!(order: :baseline)
111
- end
@@ -1,83 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/inline'
4
-
5
- gemfile do
6
- source 'https://rubygems.org'
7
- gem 'uringmachine', path: '..'
8
- gem 'benchmark-ips'
9
- end
10
-
11
- require 'benchmark/ips'
12
- require 'uringmachine'
13
-
14
- def consume_from_queue(queue)
15
- m = UM.new
16
- while true
17
- response_mailbox, closure = m.shift(queue)
18
- result = closure.call
19
- m.push(response_mailbox, result)
20
- end
21
- # rescue UM::Terminate
22
- # # We can also add a timeout here
23
- # t0 = Time.now
24
- # while !queue.empty? && (Time.now - t0) < 10
25
- # response_mailbox, closure = m.shift(queue)
26
- # result = closure.call
27
- # m.push(response_mailbox, result)
28
- # end
29
- end
30
-
31
- N = 8
32
-
33
- $machine = UM.new
34
- @queue = UM::Queue.new
35
- @threads = N.times.map { Thread.new { consume_from_queue(@queue) } }
36
-
37
- def side_run(&block)
38
- mailbox = Fiber.current.mailbox
39
- $machine.push(@queue, [mailbox, block])
40
- Thread.pass
41
- $machine.shift(mailbox)
42
- end
43
-
44
- @rqueue = Queue.new
45
- @rthreads = N.times.map { Thread.new { r_consume_from_queue(@rqueue) }}
46
-
47
- def r_consume_from_queue(queue)
48
- m = UM.new
49
- while true
50
- response_mailbox, closure = @rqueue.shift
51
- result = closure.call
52
- m.push(response_mailbox, result)
53
- end
54
- # rescue UM::Terminate
55
- # # We can also add a timeout here
56
- # t0 = Time.now
57
- # while !queue.empty? && (Time.now - t0) < 10
58
- # response_mailbox, closure = m.shift(queue)
59
- # result = closure.call
60
- # m.push(response_mailbox, result)
61
- # end
62
- end
63
-
64
- def r_side_run(&block)
65
- mailbox = Fiber.current.mailbox
66
- @rqueue.push([mailbox, block])
67
- Thread.pass
68
- $machine.shift(mailbox)
69
- end
70
-
71
- # puts '*' * 40
72
- # p r_side_run { }
73
- # exit!
74
-
75
- Benchmark.ips do |x|
76
- x.config(:time => 5, :warmup => 2)
77
-
78
- x.report("side-run") { side_run { } }
79
- x.report("r-side-run") { r_side_run { } }
80
- # x.report("snoozing") { $machine.snooze }
81
-
82
- x.compare!
83
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/inline'
4
-
5
- gemfile do
6
- source 'https://rubygems.org'
7
- gem 'uringmachine', path: '..'
8
- gem 'extralite'
9
- gem 'benchmark-ips'
10
- end
11
-
12
- require 'uringmachine'
13
- require 'extralite'
14
-
15
- class UM::Actor < Fiber
16
- def initialize(machine, target)
17
- @machine = machine
18
- @target = target
19
- @mailbox = UM::Queue.new
20
- super { act }
21
- end
22
-
23
- def act
24
- while (sym, a, k, peer = @machine.shift(@mailbox))
25
-
26
- begin
27
- ret = @target.send(sym, *a, **k)
28
- @machine.schedule(peer, ret)
29
- rescue => e
30
- @machine.schedule(peer, e)
31
- end
32
- end
33
- rescue Exception => e
34
- # handle unhandled exceptions
35
- ensure
36
- @machine.fiber_map.delete(self)
37
- @machine.yield
38
- end
39
-
40
- def method_missing(sym, *a, **k)
41
- @machine.push(@mailbox, [sym, a, k, Fiber.current])
42
- ret = @machine.yield
43
- raise(ret) if ret.is_a?(Exception)
44
- ret
45
- end
46
- end
47
-
48
- class UM
49
- def spin_actor(target)
50
- f = UM::Actor.new(self, target)
51
- schedule(f, nil)
52
- @@fiber_map[f] = f
53
- f
54
- end
55
- end
56
-
57
- class Locker
58
- def initialize(machine, target)
59
- @machine = machine
60
- @target = target
61
- @mutex = UM::Mutex.new
62
- end
63
-
64
- def method_missing(sym, *a, **k)
65
- @machine.synchronize(@mutex) { @target.send(sym, *a, **k) }
66
- end
67
- end
68
-
69
-
70
- PATH = '/tmp/foo'
71
-
72
- $machine = UM.new
73
- $raw_db = Extralite::Database.new(PATH)
74
- $actor_db = $machine.spin_actor(Extralite::Database.new(PATH))
75
- $locker_db = Locker.new($machine, Extralite::Database.new(PATH))
76
-
77
- [$raw_db, $actor_db, $locker_db].each do |db|
78
- p db.query('select 1')
79
- end
80
-
81
- bm = Benchmark.ips do |x|
82
- x.config(:time => 5, :warmup => 2)
83
-
84
- x.report("raw") { $raw_db.query('select 1') }
85
- x.report("actor") { $actor_db.query('select 1') }
86
- x.report("locker") { $locker_db.query('select 1') }
87
-
88
- x.compare!
89
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../lib/uringmachine'
4
- require 'resolv'
5
-
6
- machine = UM.new
7
-
8
- addrs = machine.resolve('status.realiteq.net')
9
-
10
- puts '*' * 40
11
- puts addrs.join("\n")
12
- puts
File without changes
File without changes
File without changes