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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +14 -0
- data/TODO.md +144 -0
- data/benchmark/README.md +173 -0
- data/benchmark/bm_io_pipe.rb +70 -0
- data/benchmark/bm_io_socketpair.rb +71 -0
- data/benchmark/bm_mutex_cpu.rb +57 -0
- data/benchmark/bm_mutex_io.rb +64 -0
- data/benchmark/bm_pg_client.rb +109 -0
- data/benchmark/bm_queue.rb +76 -0
- data/benchmark/chart.png +0 -0
- data/benchmark/common.rb +135 -0
- data/benchmark/dns_client.rb +47 -0
- data/{examples/bm_http_parse.rb → benchmark/http_parse.rb} +1 -1
- data/benchmark/run_bm.rb +8 -0
- data/benchmark/sqlite.rb +108 -0
- data/{examples/bm_write.rb → benchmark/write.rb} +4 -4
- data/ext/um/um.c +189 -100
- data/ext/um/um.h +36 -10
- data/ext/um/um_async_op.c +1 -1
- data/ext/um/um_class.c +87 -13
- data/ext/um/um_op.c +6 -0
- data/ext/um/um_sync.c +2 -2
- data/ext/um/um_utils.c +16 -0
- data/grant-2025/journal.md +118 -1
- data/grant-2025/tasks.md +48 -22
- data/lib/uringmachine/actor.rb +8 -0
- data/lib/uringmachine/dns_resolver.rb +1 -2
- data/lib/uringmachine/fiber_scheduler.rb +127 -81
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +32 -3
- data/test/helper.rb +7 -18
- data/test/test_actor.rb +12 -3
- data/test/test_async_op.rb +10 -10
- data/test/test_fiber.rb +84 -1
- data/test/test_fiber_scheduler.rb +950 -47
- data/test/test_um.rb +297 -120
- data/uringmachine.gemspec +2 -1
- metadata +38 -16
- data/examples/bm_fileno.rb +0 -33
- data/examples/bm_queue.rb +0 -111
- data/examples/bm_side_running.rb +0 -83
- data/examples/bm_sqlite.rb +0 -89
- data/examples/dns_client.rb +0 -12
- /data/{examples/bm_mutex.rb → benchmark/mutex.rb} +0 -0
- /data/{examples/bm_mutex_single.rb → benchmark/mutex_single.rb} +0 -0
- /data/{examples/bm_send.rb → benchmark/send.rb} +0 -0
- /data/{examples/bm_snooze.rb → benchmark/snooze.rb} +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative './common'
|
|
4
|
+
require 'securerandom'
|
|
5
|
+
|
|
6
|
+
C = ENV['C']&.to_i ||50
|
|
7
|
+
I = 1000
|
|
8
|
+
puts "C=#{C}"
|
|
9
|
+
|
|
10
|
+
class UMBenchmark
|
|
11
|
+
CONTAINER_NAME = "pg-#{SecureRandom.hex}"
|
|
12
|
+
|
|
13
|
+
def start_pg_server
|
|
14
|
+
`docker run --name #{CONTAINER_NAME} -e POSTGRES_PASSWORD=my_password -d -p 5432:5432 postgres`
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def stop_pg_server
|
|
18
|
+
`docker stop #{CONTAINER_NAME}`
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
PG_OPTS = {
|
|
22
|
+
host: 'localhost',
|
|
23
|
+
user: 'postgres',
|
|
24
|
+
password: 'my_password',
|
|
25
|
+
dbname: 'postgres'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def create_db_conn(retries = 0)
|
|
29
|
+
::PG.connect(PG_OPTS)
|
|
30
|
+
rescue ::PG::ConnectionBad
|
|
31
|
+
if retries < 3
|
|
32
|
+
sleep 0.5
|
|
33
|
+
create_db_conn(retries + 1)
|
|
34
|
+
else
|
|
35
|
+
raise
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
PREPARE_SQL = <<~SQL
|
|
40
|
+
create table if not exists foo(value int, name text);
|
|
41
|
+
create index if not exists idx_foo_value on foo(value);
|
|
42
|
+
with t1 as (
|
|
43
|
+
select generate_series(1,100000)
|
|
44
|
+
),
|
|
45
|
+
t2 as (
|
|
46
|
+
select (random()*1000000)::integer as value,
|
|
47
|
+
(md5(random()::text)) as name
|
|
48
|
+
from t1
|
|
49
|
+
)
|
|
50
|
+
insert into foo(value, name) select * from t2;
|
|
51
|
+
SQL
|
|
52
|
+
|
|
53
|
+
def prepare_db
|
|
54
|
+
STDOUT << "Preparing database..."
|
|
55
|
+
t0 = Time.now
|
|
56
|
+
conn = create_db_conn
|
|
57
|
+
conn.exec(PREPARE_SQL)
|
|
58
|
+
puts " elapsed: #{Time.now - t0}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def query_db(conn)
|
|
62
|
+
min = rand(1000000)
|
|
63
|
+
max = min + 2000
|
|
64
|
+
sql = format(
|
|
65
|
+
'select * from foo where value >= %d and value < %d;', min, max
|
|
66
|
+
)
|
|
67
|
+
conn.query(sql).to_a
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def with_container
|
|
71
|
+
start_pg_server
|
|
72
|
+
sleep 0.5
|
|
73
|
+
prepare_db
|
|
74
|
+
yield
|
|
75
|
+
rescue Exception => e
|
|
76
|
+
p e
|
|
77
|
+
p e.backtrace
|
|
78
|
+
ensure
|
|
79
|
+
stop_pg_server
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def benchmark
|
|
83
|
+
with_container {
|
|
84
|
+
Benchmark.bm { run_benchmarks(it) }
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def do_threads(threads, ios)
|
|
89
|
+
C.times.map do
|
|
90
|
+
threads << Thread.new do
|
|
91
|
+
conn = create_db_conn
|
|
92
|
+
I.times { query_db(conn) }
|
|
93
|
+
ensure
|
|
94
|
+
conn.close
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def do_scheduler(scheduler, ios)
|
|
100
|
+
C.times do
|
|
101
|
+
Fiber.schedule do
|
|
102
|
+
conn = create_db_conn
|
|
103
|
+
I.times { query_db(conn) }
|
|
104
|
+
ensure
|
|
105
|
+
conn.close
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative './common'
|
|
4
|
+
|
|
5
|
+
GROUPS = 40
|
|
6
|
+
PRODUCERS = 5
|
|
7
|
+
CONSUMERS = 10
|
|
8
|
+
ITEMS = 200000
|
|
9
|
+
|
|
10
|
+
class UMBenchmark
|
|
11
|
+
def do_threads(threads, ios)
|
|
12
|
+
GROUPS.times do
|
|
13
|
+
queue = Queue.new
|
|
14
|
+
PRODUCERS.times do
|
|
15
|
+
threads << Thread.new do
|
|
16
|
+
ITEMS.times { queue << rand(1000) }
|
|
17
|
+
CONSUMERS.times { queue << :stop }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
CONSUMERS.times do
|
|
21
|
+
threads << Thread.new do
|
|
22
|
+
loop do
|
|
23
|
+
item = queue.shift
|
|
24
|
+
break if item == :stop
|
|
25
|
+
|
|
26
|
+
item * rand(1000)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def do_scheduler(scheduler, ios)
|
|
34
|
+
GROUPS.times do
|
|
35
|
+
queue = Queue.new
|
|
36
|
+
PRODUCERS.times do
|
|
37
|
+
Fiber.schedule do
|
|
38
|
+
ITEMS.times { queue << rand(1000) }
|
|
39
|
+
CONSUMERS.times { queue << :stop }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
CONSUMERS.times do
|
|
43
|
+
Fiber.schedule do
|
|
44
|
+
loop do
|
|
45
|
+
item = queue.shift
|
|
46
|
+
break if item == :stop
|
|
47
|
+
|
|
48
|
+
item * rand(1000)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def do_um(machine, fibers, fds)
|
|
56
|
+
GROUPS.times do
|
|
57
|
+
queue = UM::Queue.new
|
|
58
|
+
PRODUCERS.times do
|
|
59
|
+
fibers << machine.spin do
|
|
60
|
+
ITEMS.times { machine.push(queue, rand(1000)) }
|
|
61
|
+
CONSUMERS.times { machine.push(queue, :stop) }
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
CONSUMERS.times do
|
|
65
|
+
fibers << machine.spin do
|
|
66
|
+
loop do
|
|
67
|
+
item = machine.shift(queue)
|
|
68
|
+
break if item == :stop
|
|
69
|
+
|
|
70
|
+
item * rand(1000)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/benchmark/chart.png
ADDED
|
Binary file
|
data/benchmark/common.rb
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
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 'io-event'
|
|
10
|
+
gem 'async'
|
|
11
|
+
gem 'pg'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
require 'uringmachine/fiber_scheduler'
|
|
15
|
+
|
|
16
|
+
class WorkerThreadPool
|
|
17
|
+
def initialize(size)
|
|
18
|
+
@size = size
|
|
19
|
+
@queue = Queue.new
|
|
20
|
+
setup_threads
|
|
21
|
+
sleep 0.01 * @size
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def queue(&block)
|
|
25
|
+
@queue << block
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def join
|
|
29
|
+
@size.times { @queue << :stop }
|
|
30
|
+
@threads.each(&:join)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def setup_threads
|
|
34
|
+
@threads = @size.times.map {
|
|
35
|
+
Thread.new do
|
|
36
|
+
loop do
|
|
37
|
+
job = @queue.shift
|
|
38
|
+
break if job == :stop
|
|
39
|
+
|
|
40
|
+
job.()
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class UMBenchmark
|
|
48
|
+
def initialize
|
|
49
|
+
@thread_pool = WorkerThreadPool.new(10)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def benchmark
|
|
53
|
+
Benchmark.bm { run_benchmarks(it) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@@benchmarks = {
|
|
57
|
+
threads: [:threads, "Threads"],
|
|
58
|
+
thread_pool: [:thread_pool, "ThreadPool"],
|
|
59
|
+
async_uring: [:scheduler, "Async uring"],
|
|
60
|
+
async_epoll: [:scheduler, "Async epoll"],
|
|
61
|
+
um_fs: [:scheduler, "UM FS"],
|
|
62
|
+
um: [:um, "UM"],
|
|
63
|
+
um_sqpoll: [:um, "UM sqpoll"]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
def run_benchmarks(b)
|
|
67
|
+
@@benchmarks.each do |sym, (doer, name)|
|
|
68
|
+
b.report(name) { send(:"run_#{sym}") } if respond_to?(:"do_#{doer}")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def run_threads
|
|
73
|
+
threads = []
|
|
74
|
+
ios = []
|
|
75
|
+
do_threads(threads, ios)
|
|
76
|
+
threads.each(&:join)
|
|
77
|
+
ios.each { it.close rescue nil }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def run_thread_pool
|
|
81
|
+
ios = []
|
|
82
|
+
do_thread_pool(@thread_pool, ios)
|
|
83
|
+
@thread_pool.join
|
|
84
|
+
ios.each { it.close rescue nil }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def run_async_uring
|
|
88
|
+
selector ||= IO::Event::Selector::URing.new(Fiber.current)
|
|
89
|
+
scheduler = Async::Scheduler.new(selector:)
|
|
90
|
+
Fiber.set_scheduler(scheduler)
|
|
91
|
+
ios = []
|
|
92
|
+
scheduler.run { do_scheduler(scheduler, ios) }
|
|
93
|
+
ios.each { it.close rescue nil }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def run_async_epoll
|
|
97
|
+
selector ||= IO::Event::Selector::EPoll.new(Fiber.current)
|
|
98
|
+
scheduler = Async::Scheduler.new(selector:)
|
|
99
|
+
Fiber.set_scheduler(scheduler)
|
|
100
|
+
ios = []
|
|
101
|
+
scheduler.run { do_scheduler(scheduler, ios) }
|
|
102
|
+
ios.each { it.close rescue nil }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def run_um_fs
|
|
106
|
+
machine = UM.new
|
|
107
|
+
scheduler = UM::FiberScheduler.new(machine)
|
|
108
|
+
Fiber.set_scheduler(scheduler)
|
|
109
|
+
ios = []
|
|
110
|
+
do_scheduler(scheduler, ios)
|
|
111
|
+
scheduler.join
|
|
112
|
+
ios.each { it.close rescue nil }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def run_um
|
|
116
|
+
machine = UM.new(4096)
|
|
117
|
+
fibers = []
|
|
118
|
+
fds = []
|
|
119
|
+
do_um(machine, fibers, fds)
|
|
120
|
+
machine.await_fibers(fibers)
|
|
121
|
+
fds.each { machine.close(it) }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def run_um_sqpoll
|
|
125
|
+
machine = UM.new(4096, true)
|
|
126
|
+
fibers = []
|
|
127
|
+
fds = []
|
|
128
|
+
do_um(machine, fibers, fds)
|
|
129
|
+
machine.await_fibers(fibers)
|
|
130
|
+
fds.each { machine.close_async(it) }
|
|
131
|
+
machine.snooze
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
at_exit { UMBenchmark.new.benchmark }
|
|
@@ -0,0 +1,47 @@
|
|
|
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 'uringmachine'
|
|
13
|
+
require 'resolv'
|
|
14
|
+
|
|
15
|
+
def do_addrinfo
|
|
16
|
+
Addrinfo.tcp("status.realiteq.net", 80)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def do_resolv
|
|
20
|
+
Resolv.getaddresses('status.realiteq.net')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
@machine = UM.new
|
|
24
|
+
def do_um
|
|
25
|
+
@machine.resolve('status.realiteq.net')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# p do_addrinfo
|
|
29
|
+
# p do_resolv
|
|
30
|
+
# p do_um
|
|
31
|
+
# exit
|
|
32
|
+
|
|
33
|
+
Benchmark.ips do |x|
|
|
34
|
+
x.report("Addrinfo") { do_addrinfo }
|
|
35
|
+
x.report("resolv") { do_resolv }
|
|
36
|
+
x.report("UM.resolve") { do_um }
|
|
37
|
+
|
|
38
|
+
x.compare!(order: :baseline)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# addrs = machine.resolve('status.realiteq.net')
|
|
44
|
+
|
|
45
|
+
# puts '*' * 40
|
|
46
|
+
# puts addrs.join("\n")
|
|
47
|
+
# puts
|
data/benchmark/run_bm.rb
ADDED
data/benchmark/sqlite.rb
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
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'
|
|
10
|
+
gem 'benchmark-ips'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
require 'uringmachine'
|
|
14
|
+
require 'extralite'
|
|
15
|
+
require 'benchmark/ips'
|
|
16
|
+
require 'securerandom'
|
|
17
|
+
require 'uringmachine/actor'
|
|
18
|
+
|
|
19
|
+
class DBActor2
|
|
20
|
+
def initialize(machine, db)
|
|
21
|
+
@machine = machine
|
|
22
|
+
@db = db
|
|
23
|
+
@mailbox = UM::Queue.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def act
|
|
27
|
+
while (a, k, peer = @machine.shift(@mailbox))
|
|
28
|
+
|
|
29
|
+
begin
|
|
30
|
+
ret = @db.query(*a, **k)
|
|
31
|
+
@machine.schedule(peer, ret)
|
|
32
|
+
rescue => e
|
|
33
|
+
@machine.schedule(peer, e)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
rescue Exception => e
|
|
37
|
+
# handle unhandled exceptions
|
|
38
|
+
ensure
|
|
39
|
+
@machine.fiber_map.delete(self)
|
|
40
|
+
@machine.yield
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def query(*a, **k)
|
|
44
|
+
@machine.push(@mailbox, [a, k, Fiber.current])
|
|
45
|
+
ret = @machine.yield
|
|
46
|
+
raise(ret) if ret.is_a?(Exception)
|
|
47
|
+
ret
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.spin(machine, db)
|
|
51
|
+
actor = new(machine, db)
|
|
52
|
+
machine.spin { actor.act }
|
|
53
|
+
actor
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
PATH = "/tmp/um_bm_sqlite_#{SecureRandom.hex}"
|
|
58
|
+
|
|
59
|
+
module DBActor
|
|
60
|
+
def setup(db)
|
|
61
|
+
@db = db
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def query(*, **)
|
|
65
|
+
@db.query(*, **)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class Locker
|
|
70
|
+
def initialize(machine, target)
|
|
71
|
+
@machine = machine
|
|
72
|
+
@target = target
|
|
73
|
+
@mutex = UM::Mutex.new
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def method_missing(sym, *a, **k)
|
|
77
|
+
@machine.synchronize(@mutex) { @target.send(sym, *a, **k) }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def prepare_db(machine, path)
|
|
82
|
+
Extralite::Database.new(path).tap {
|
|
83
|
+
it.on_progress(mode: :at_least_once, period: 100, tick: 10) { machine.snooze }
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
machine = UM.new
|
|
88
|
+
mailbox = UM::Queue.new
|
|
89
|
+
raw_db = prepare_db(machine, PATH)
|
|
90
|
+
actor_db = machine.spin_actor(DBActor, prepare_db(machine, PATH))
|
|
91
|
+
actor2_db = DBActor2.spin(machine, prepare_db(machine, PATH))
|
|
92
|
+
locker_db = Locker.new(machine, prepare_db(machine, PATH))
|
|
93
|
+
|
|
94
|
+
p raw_db.query('select 1')
|
|
95
|
+
p actor_db.call(mailbox, :query, 'select 1')
|
|
96
|
+
p actor2_db.query('select 1')
|
|
97
|
+
p locker_db.query('select 1')
|
|
98
|
+
|
|
99
|
+
bm = Benchmark.ips do |x|
|
|
100
|
+
x.config(:time => 5, :warmup => 2)
|
|
101
|
+
|
|
102
|
+
x.report("raw") { raw_db.query('select 1') }
|
|
103
|
+
x.report("actor") { actor_db.call(mailbox, :query, 'select 1') }
|
|
104
|
+
x.report("actor2") { actor2_db.query('select 1') }
|
|
105
|
+
x.report("locker") { locker_db.query('select 1') }
|
|
106
|
+
|
|
107
|
+
x.compare!(order: :baseline)
|
|
108
|
+
end
|
|
@@ -11,7 +11,7 @@ end
|
|
|
11
11
|
require 'benchmark'
|
|
12
12
|
require 'uringmachine'
|
|
13
13
|
|
|
14
|
-
ITERATIONS =
|
|
14
|
+
ITERATIONS = 100000
|
|
15
15
|
BUF = ('*' * 8192).freeze
|
|
16
16
|
FN = '/tmp/bm_write'
|
|
17
17
|
|
|
@@ -50,10 +50,10 @@ end
|
|
|
50
50
|
run_io_write(1)
|
|
51
51
|
run_um_write(1)
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
[1, 2, 4, 8].each do |c|
|
|
54
|
+
Benchmark.bm do |x|
|
|
55
55
|
x.report("IO (#{c} threads)") { run_io_write(c) }
|
|
56
56
|
x.report("UM (#{c} fibers) ") { run_um_write(c) }
|
|
57
|
-
puts
|
|
58
57
|
end
|
|
58
|
+
puts
|
|
59
59
|
end
|