uringmachine 0.22.1 → 0.23.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/CHANGELOG.md +12 -2
- data/TODO.md +24 -138
- data/benchmark/README.md +1 -1
- data/benchmark/bm_io_pipe.rb +14 -0
- data/benchmark/common.rb +5 -0
- data/benchmark/read_each.rb +83 -0
- data/benchmark/send.rb +31 -36
- data/ext/um/extconf.rb +7 -1
- data/ext/um/um.c +131 -8
- data/ext/um/um.h +8 -0
- data/ext/um/um_class.c +34 -0
- data/ext/um/um_const.c +0 -2
- data/ext/um/um_op.c +20 -2
- data/ext/um/um_utils.c +27 -0
- data/grant-2025/journal.md +2 -2
- data/grant-2025/tasks.md +7 -12
- data/lib/uringmachine/version.rb +1 -1
- data/test/helper.rb +5 -4
- data/test/test_fiber_scheduler.rb +1 -17
- data/test/test_um.rb +299 -62
- data/vendor/liburing/configure +4 -2
- data/vendor/liburing/src/Makefile +1 -0
- data/vendor/liburing/test/min-timeout-wait.c +57 -2
- data/vendor/liburing/test/min-timeout.c +22 -0
- metadata +4 -3
- /data/benchmark/{chart.png → chart_all.png} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b185b9cafdee3930061ed7101a12ccb500a8f131a9715a6a1268b22507ec2d85
|
|
4
|
+
data.tar.gz: c2d0fe4aced8f2340b2cdd29cf9540b7075198be56a0e695f58b9fbab5fae65c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75b0fe0a71242d728cbe1901457e41a11255c1922eca6501e3fa4286a7bd89a01ad7e5625baa24721ad63efc16da24a4b4d8ffeb85114be8fbe910066a0b033e
|
|
7
|
+
data.tar.gz: 64afeb65cc42c5b5c3af30dc361f2917a6341fd38e645114c48972bb6aa6617f496be6de040dfec6eefa102ae1d933cd726549890a9d57369f465be3b6df613f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
# 0.23.0 2025-12-16
|
|
3
|
+
|
|
4
|
+
- Add `UM#accept_into_queue`, fix `#accept_each` to throw on error
|
|
5
|
+
- Use Set instead of Hash for holding pending fibers
|
|
6
|
+
- Add `UM#writev`, `UM#sendv` methods
|
|
7
|
+
- Allocate um_op and um_op_result in batches of 256
|
|
8
|
+
- Remove `SIGCLD` const
|
|
9
|
+
|
|
1
10
|
# 0.22.1 2025-12-11
|
|
2
11
|
|
|
3
12
|
- Comment out SIGCLD constant
|
|
@@ -12,6 +21,7 @@
|
|
|
12
21
|
- More tests and benchmarks
|
|
13
22
|
- Add `UM#await_fibers` for awaiting fibers
|
|
14
23
|
- Add `UM.socketpair` for creating a socket pair
|
|
24
|
+
- Fix segfault caused by waiting fibers not being marked
|
|
15
25
|
- Fiber scheduler:
|
|
16
26
|
- Use fiber's mailbox for processing blocking operations
|
|
17
27
|
- Add `#io_close`, `#yield` hooks, remove `#process_fork` hook
|
|
@@ -26,8 +36,8 @@
|
|
|
26
36
|
- Add debug logging for key io_uring interactions
|
|
27
37
|
- Add UM#mark and DEBUG_MARK for debugging specific UM instances
|
|
28
38
|
- Short-circuit zero-length writes
|
|
29
|
-
- Add optional file_offset argument to #read, #write. Add optional len and
|
|
30
|
-
|
|
39
|
+
- Add optional file_offset argument to #read, #write. Add optional len and
|
|
40
|
+
file_offset arguments to #write_async
|
|
31
41
|
- Add support for specifying SQPOLL mode and SQ idle timeout in `UM#initialize`
|
|
32
42
|
- Add support for specifying number of SQ entries in `UM#initialize`
|
|
33
43
|
- Implement global worker pool for blocking operations in fiber scheduler
|
data/TODO.md
CHANGED
|
@@ -1,148 +1,28 @@
|
|
|
1
1
|
## immediate
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
- use CPU time (CLOCK_THREAD_CPUTIME_ID)
|
|
6
|
-
- measure:
|
|
7
|
-
- time each fiber is waiting
|
|
8
|
-
- time each fiber is running
|
|
9
|
-
- time machine is waiting (for CQEs)
|
|
10
|
-
- time machine is running fibers from the runqueue
|
|
11
|
-
- can be turned on/off at any time
|
|
12
|
-
- no performance impact when off
|
|
13
|
-
|
|
14
|
-
How can this be implemented:
|
|
15
|
-
|
|
16
|
-
- `um_get_time_cpu()` function for reading CPU time (CLOCK_THREAD_CPUTIME_ID) as
|
|
17
|
-
double.
|
|
18
|
-
- add to `struct um`:
|
|
19
|
-
|
|
20
|
-
```c
|
|
21
|
-
struct um {
|
|
22
|
-
...
|
|
23
|
-
int profiling_mode;
|
|
24
|
-
double total_time_run;
|
|
25
|
-
double total_time_wait;
|
|
26
|
-
double last_cpu_time;
|
|
27
|
-
}
|
|
28
|
-
```
|
|
3
|
+
## buffer rings - automatic management
|
|
29
4
|
|
|
30
|
-
|
|
31
|
-
|
|
5
|
+
```ruby
|
|
6
|
+
# completely hands off
|
|
7
|
+
machine.read_each(fd) { |str| ... }
|
|
32
8
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
machine->last_cpu_time = um_get_time_cpu();
|
|
37
|
-
```
|
|
9
|
+
# what if we want to get IO::Buffer?
|
|
10
|
+
machine.read_each(fd, io_buffer: true) { |iobuff, len| ... }
|
|
11
|
+
```
|
|
38
12
|
|
|
39
|
-
|
|
40
|
-
- before processing CQEs:
|
|
41
|
-
|
|
42
|
-
```c
|
|
43
|
-
// before
|
|
44
|
-
double cpu_time0;
|
|
45
|
-
VALUE fiber;
|
|
46
|
-
int profiling_mode = machine->profiling_mode;
|
|
47
|
-
if (profiling_mode) {
|
|
48
|
-
fiber = rb_fiber_current();
|
|
49
|
-
cpu_time0 = um_get_time_cpu();
|
|
50
|
-
double elapsed = cpu_time0 - machine->last_cpu_time;
|
|
51
|
-
um_update_fiber_time_run(fiber, cpu_time0, elapsed);
|
|
52
|
-
machine->total_time_run += elapsed;
|
|
53
|
-
}
|
|
54
|
-
process_cqes(...)
|
|
55
|
-
// after
|
|
56
|
-
if (profiling_mode) {
|
|
57
|
-
double cpu_time1 = um_get_time_cpu();
|
|
58
|
-
double elapsed = cpu_time1 - cpu_time0;
|
|
59
|
-
um_update_fiber_last_time(fiber, cpu_time1);
|
|
60
|
-
machine->total_time_wait += elapsed;
|
|
61
|
-
machine->last_cpu_time = cpu_time1;
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
- when doing switching, in `um_process_runqueue_op`:
|
|
66
|
-
|
|
67
|
-
```c
|
|
68
|
-
// before
|
|
69
|
-
double cpu_time;
|
|
70
|
-
VALUE cur_fiber;
|
|
71
|
-
VALUE next_fiber = get_next_fiber(...);
|
|
72
|
-
int profiling_mode = machine->profiling_mode;
|
|
73
|
-
if (profiling_mode) {
|
|
74
|
-
cur_fiber = rb_fiber_current();
|
|
75
|
-
cpu_time = um_get_time_cpu();
|
|
76
|
-
double elapsed = cpu_time - machine->last_cpu_time;
|
|
77
|
-
um_update_fiber_time_run(cur_fiber, cpu_time, elapsed);
|
|
78
|
-
machine->total_time_run += elapsed;
|
|
79
|
-
um_update_fiber_time_wait(next_fiber, cpu_time);
|
|
80
|
-
machine->last_cpu_time = cpu_time;
|
|
81
|
-
}
|
|
82
|
-
do_fiber_transfer(...)
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
- updating fiber time instance vars:
|
|
86
|
-
|
|
87
|
-
```c
|
|
88
|
-
inline void um_update_fiber_time_run(VALUE fiber, double stamp, double elapsed) {
|
|
89
|
-
// VALUE fiber_stamp = rb_ivar_get(fiber, ID_time_last_cpu);
|
|
90
|
-
VALUE fiber_total_run = rb_ivar_get(fiber, ID_time_total_run);
|
|
91
|
-
double total = NIL_P(fiber_total_run) ?
|
|
92
|
-
elapsed : NUM2DBL(fiber_total_run) + elapsed;
|
|
93
|
-
rb_ivar_set(fiber, ID_time_total_run, DBL2NUM(total));
|
|
94
|
-
rb_ivar_set(fiber, ID_time_last_cpu, DBL2NUM(stamp));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
inline void um_update_fiber_time_wait(VALUE fiber, double stamp) {
|
|
98
|
-
VALUE fiber_last_stamp = rb_ivar_get(fiber, ID_time_last_cpu);
|
|
99
|
-
if (likely(!NIL_P(fiber_last_stamp))) {
|
|
100
|
-
double last_stamp = NUM2DBL(fiber_last_stamp);
|
|
101
|
-
double elapsed = stamp - last_stamp;
|
|
102
|
-
VALUE fiber_total_wait = rb_ivar_get(fiber, ID_time_total_wait);
|
|
103
|
-
double total = NIL_P(fiber_total_wait) ?
|
|
104
|
-
elapsed : NUM2DBL(fiber_total_wait) + elapsed;
|
|
105
|
-
rb_ivar_set(fiber, ID_time_total_wait, DBL2NUM(total));
|
|
106
|
-
}
|
|
107
|
-
else
|
|
108
|
-
rb_ivar_set(fiber, ID_time_total_wait, DBL2NUM(0.0));
|
|
109
|
-
rb_ivar_set(fiber, ID_time_last_cpu, DBL2NUM(stamp));
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## Metrics API
|
|
114
|
-
|
|
115
|
-
- machine metrics: `UM#metrics` - returns a hash containing metrics:
|
|
13
|
+
## write/send multiple buffers at once
|
|
116
14
|
|
|
117
|
-
|
|
118
|
-
{
|
|
119
|
-
size:, # SQ size (entries)
|
|
120
|
-
total_ops:, # total ops submitted
|
|
121
|
-
total_fiber_switches:, # total fiber switches
|
|
122
|
-
total_cqe_waits:, # total number of CQE waits
|
|
123
|
-
ops_pending:, # number of pending ops
|
|
124
|
-
ops_unsubmitted:, # number of unsubmitted
|
|
125
|
-
ops_runqueue:, # number of ops in runqueue
|
|
126
|
-
ops_free:, # number of ops in freelist
|
|
127
|
-
ops_transient:, # number of ops in transient list
|
|
128
|
-
hwm_pending:, # high water mark - pending ops
|
|
129
|
-
hwm_unsubmitted:, # high water mark - unsubmitted ops
|
|
130
|
-
hwm_runqueue:, # high water mark - runqueue depth
|
|
131
|
-
hwm_free:, # high water mark - ops in free list
|
|
132
|
-
hwm_transient:, # high water mark - ops in transient list
|
|
133
|
-
# when profiling is active
|
|
134
|
-
time_total_run:, # total CPU time running
|
|
135
|
-
time_total_wait:, # total CPU time waiting for CQEs
|
|
136
|
-
}
|
|
137
|
-
```
|
|
15
|
+
This is done as vectored IO:
|
|
138
16
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
- transient list size
|
|
142
|
-
- free list size
|
|
143
|
-
- Those will be done in um_op.c (in linked list management code)
|
|
17
|
+
```ruby
|
|
18
|
+
machine.writev(fd, buf1, buf2, buf3)
|
|
144
19
|
|
|
145
|
-
|
|
20
|
+
# with optional file offset:
|
|
21
|
+
machine.writev(fd, buf1, buf2, buf3, 0)
|
|
22
|
+
|
|
23
|
+
# for the moment it won't take flags
|
|
24
|
+
machine.sendv(fd, buf1, buf2, buf3)
|
|
25
|
+
```
|
|
146
26
|
|
|
147
27
|
## useful concurrency tools
|
|
148
28
|
|
|
@@ -152,13 +32,19 @@ How can this be implemented:
|
|
|
152
32
|
debouncer = UM.debounce { }
|
|
153
33
|
```
|
|
154
34
|
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## polyvalent select
|
|
38
|
+
|
|
39
|
+
- select on multiple queues (ala Go)
|
|
40
|
+
- select on mixture of queues and fds
|
|
41
|
+
|
|
155
42
|
## ops
|
|
156
43
|
|
|
157
44
|
- [ ] multishot timeout
|
|
158
45
|
- [v] machine.periodically(interval) { ... }
|
|
159
46
|
- [ ] machine.prep_timeout_multishot(interval)
|
|
160
47
|
|
|
161
|
-
- writev
|
|
162
48
|
- splice / - tee
|
|
163
49
|
- sendto
|
|
164
50
|
- recvfrom
|
data/benchmark/README.md
CHANGED
data/benchmark/bm_io_pipe.rb
CHANGED
|
@@ -38,6 +38,20 @@ class UMBenchmark
|
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
+
def do_baseline
|
|
42
|
+
GROUPS.times do
|
|
43
|
+
r, w = IO.pipe
|
|
44
|
+
r.sync = true
|
|
45
|
+
w.sync = true
|
|
46
|
+
ITERATIONS.times {
|
|
47
|
+
w.write(DATA)
|
|
48
|
+
r.read(SIZE)
|
|
49
|
+
}
|
|
50
|
+
r.close
|
|
51
|
+
w.close
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
41
55
|
def do_scheduler(scheduler, ios)
|
|
42
56
|
GROUPS.times do
|
|
43
57
|
r, w = IO.pipe
|
data/benchmark/common.rb
CHANGED
|
@@ -54,6 +54,7 @@ class UMBenchmark
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
@@benchmarks = {
|
|
57
|
+
baseline: [:baseline, "No Concurrency"],
|
|
57
58
|
threads: [:threads, "Threads"],
|
|
58
59
|
thread_pool: [:thread_pool, "ThreadPool"],
|
|
59
60
|
async_uring: [:scheduler, "Async uring"],
|
|
@@ -69,6 +70,10 @@ class UMBenchmark
|
|
|
69
70
|
end
|
|
70
71
|
end
|
|
71
72
|
|
|
73
|
+
def run_baseline
|
|
74
|
+
do_baseline
|
|
75
|
+
end
|
|
76
|
+
|
|
72
77
|
def run_threads
|
|
73
78
|
threads = []
|
|
74
79
|
ios = []
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
@machine = UM.new
|
|
15
|
+
|
|
16
|
+
make_socket_pair = -> do
|
|
17
|
+
port = 10000 + rand(30000)
|
|
18
|
+
server_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
19
|
+
@machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
|
|
20
|
+
@machine.bind(server_fd, '127.0.0.1', port)
|
|
21
|
+
@machine.listen(server_fd, UM::SOMAXCONN)
|
|
22
|
+
|
|
23
|
+
client_conn_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
24
|
+
@machine.connect(client_conn_fd, '127.0.0.1', port)
|
|
25
|
+
|
|
26
|
+
server_conn_fd = @machine.accept(server_fd)
|
|
27
|
+
|
|
28
|
+
@machine.close(server_fd)
|
|
29
|
+
[client_conn_fd, server_conn_fd]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
@client_fd, @server_fd = make_socket_pair.()
|
|
33
|
+
|
|
34
|
+
@read_buf = +''
|
|
35
|
+
@read_fiber = @machine.spin do
|
|
36
|
+
while true
|
|
37
|
+
@machine.read(@client_fd, @read_buf, 65536, 0)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
STR_COUNT = ARGV[0]&.to_i || 3
|
|
42
|
+
STR_SIZE = ARGV[1]&.to_i || 100
|
|
43
|
+
|
|
44
|
+
@parts = ['*' * STR_SIZE] * STR_COUNT
|
|
45
|
+
|
|
46
|
+
@server_io = IO.new(@server_fd)
|
|
47
|
+
@server_io.sync = true
|
|
48
|
+
def io_write
|
|
49
|
+
@server_io.write(*@parts)
|
|
50
|
+
@machine.snooze
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def um_write
|
|
54
|
+
str = @parts.join
|
|
55
|
+
len = str.bytesize
|
|
56
|
+
|
|
57
|
+
while len > 0
|
|
58
|
+
ret = @machine.write(@server_fd, str, len)
|
|
59
|
+
len -= ret
|
|
60
|
+
str = str[ret..-1] if len > 0
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def um_send
|
|
65
|
+
str = @parts.join
|
|
66
|
+
@machine.send(@server_fd, str, str.bytesize, UM::MSG_WAITALL)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
@bgid = @machine.setup_buffer_ring(0, 8)
|
|
70
|
+
def um_send_bundle
|
|
71
|
+
@machine.send_bundle(@server_fd, @bgid, @parts)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
p(STR_COUNT:, STR_SIZE:)
|
|
75
|
+
|
|
76
|
+
Benchmark.ips do |x|
|
|
77
|
+
x.report('IO#write') { io_write }
|
|
78
|
+
x.report('UM#write') { um_write }
|
|
79
|
+
x.report('UM#send') { um_send }
|
|
80
|
+
x.report('UM#send_bundle') { um_send_bundle }
|
|
81
|
+
|
|
82
|
+
x.compare!(order: :baseline)
|
|
83
|
+
end
|
data/benchmark/send.rb
CHANGED
|
@@ -5,49 +5,21 @@ require 'bundler/inline'
|
|
|
5
5
|
gemfile do
|
|
6
6
|
source 'https://rubygems.org'
|
|
7
7
|
gem 'uringmachine', path: '..'
|
|
8
|
+
gem 'benchmark'
|
|
8
9
|
gem 'benchmark-ips'
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
require 'benchmark/ips'
|
|
12
13
|
require 'uringmachine'
|
|
14
|
+
require 'securerandom'
|
|
13
15
|
|
|
14
16
|
@machine = UM.new
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
server_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
19
|
-
@machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
|
|
20
|
-
@machine.bind(server_fd, '127.0.0.1', port)
|
|
21
|
-
@machine.listen(server_fd, UM::SOMAXCONN)
|
|
18
|
+
@parts = ['1' * 256, '2' * (1 << 14), '3' * 256]
|
|
19
|
+
@total_len = @parts.map(&:bytesize).reduce(:+)
|
|
22
20
|
|
|
23
|
-
client_conn_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
24
|
-
@machine.connect(client_conn_fd, '127.0.0.1', port)
|
|
25
|
-
|
|
26
|
-
server_conn_fd = @machine.accept(server_fd)
|
|
27
|
-
|
|
28
|
-
@machine.close(server_fd)
|
|
29
|
-
[client_conn_fd, server_conn_fd]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
@client_fd, @server_fd = make_socket_pair.()
|
|
33
|
-
|
|
34
|
-
@read_buf = +''
|
|
35
|
-
@read_fiber = @machine.spin do
|
|
36
|
-
while true
|
|
37
|
-
@machine.read(@client_fd, @read_buf, 65536, 0)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
STR_COUNT = ARGV[0]&.to_i || 3
|
|
42
|
-
STR_SIZE = ARGV[1]&.to_i || 100
|
|
43
|
-
|
|
44
|
-
@parts = ['*' * STR_SIZE] * STR_COUNT
|
|
45
|
-
|
|
46
|
-
@server_io = IO.new(@server_fd)
|
|
47
|
-
@server_io.sync = true
|
|
48
21
|
def io_write
|
|
49
22
|
@server_io.write(*@parts)
|
|
50
|
-
@machine.snooze
|
|
51
23
|
end
|
|
52
24
|
|
|
53
25
|
def um_write
|
|
@@ -55,28 +27,51 @@ def um_write
|
|
|
55
27
|
len = str.bytesize
|
|
56
28
|
|
|
57
29
|
while len > 0
|
|
58
|
-
ret = @machine.write(@
|
|
30
|
+
ret = @machine.write(@s2, str, len)
|
|
59
31
|
len -= ret
|
|
60
32
|
str = str[ret..-1] if len > 0
|
|
61
33
|
end
|
|
62
34
|
end
|
|
63
35
|
|
|
36
|
+
def um_writev
|
|
37
|
+
@machine.writev(@s2, *@parts)
|
|
38
|
+
end
|
|
39
|
+
|
|
64
40
|
def um_send
|
|
65
41
|
str = @parts.join
|
|
66
|
-
@machine.send(@
|
|
42
|
+
@machine.send(@s2, str, str.bytesize, UM::MSG_WAITALL)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def um_sendv
|
|
46
|
+
@machine.sendv(@s2, *@parts)
|
|
67
47
|
end
|
|
68
48
|
|
|
69
49
|
@bgid = @machine.setup_buffer_ring(0, 8)
|
|
70
50
|
def um_send_bundle
|
|
71
|
-
@machine.send_bundle(@
|
|
51
|
+
@machine.send_bundle(@s2, @bgid, @parts)
|
|
72
52
|
end
|
|
73
53
|
|
|
74
|
-
|
|
54
|
+
PORT = SecureRandom.rand(10001..60001)
|
|
55
|
+
|
|
56
|
+
pid = fork { `nc -l localhost #{PORT} > /dev/null` }
|
|
57
|
+
at_exit {
|
|
58
|
+
Process.kill(:SIGKILL, pid)
|
|
59
|
+
Process.wait(pid)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
sleep 0.5
|
|
63
|
+
@s2 = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
64
|
+
@machine.connect(@s2, '127.0.0.1', PORT)
|
|
65
|
+
@server_io = IO.for_fd(@s2)
|
|
66
|
+
@server_io.sync = true
|
|
67
|
+
|
|
75
68
|
|
|
76
69
|
Benchmark.ips do |x|
|
|
77
70
|
x.report('IO#write') { io_write }
|
|
78
71
|
x.report('UM#write') { um_write }
|
|
72
|
+
x.report('UM#writev') { um_writev }
|
|
79
73
|
x.report('UM#send') { um_send }
|
|
74
|
+
x.report('UM#sendv') { um_sendv }
|
|
80
75
|
x.report('UM#send_bundle') { um_send_bundle }
|
|
81
76
|
|
|
82
77
|
x.compare!(order: :baseline)
|
data/ext/um/extconf.rb
CHANGED
|
@@ -24,7 +24,8 @@ def get_config
|
|
|
24
24
|
{
|
|
25
25
|
kernel_version: combined_version,
|
|
26
26
|
prep_bind: combined_version >= 611,
|
|
27
|
-
prep_listen: combined_version >= 611
|
|
27
|
+
prep_listen: combined_version >= 611,
|
|
28
|
+
send_vectoized: combined_version >= 617
|
|
28
29
|
}
|
|
29
30
|
end
|
|
30
31
|
|
|
@@ -45,6 +46,10 @@ if !find_header 'liburing.h', File.join(liburing_path, 'src/include')
|
|
|
45
46
|
raise "Couldn't find liburing.h"
|
|
46
47
|
end
|
|
47
48
|
|
|
49
|
+
if !find_header 'liburing/io_uring.h', File.join(liburing_path, 'src/include')
|
|
50
|
+
raise "Couldn't find liburing/io_uring.h"
|
|
51
|
+
end
|
|
52
|
+
|
|
48
53
|
if !find_library('uring', nil, File.join(liburing_path, 'src'))
|
|
49
54
|
raise "Couldn't find liburing.a"
|
|
50
55
|
end
|
|
@@ -54,6 +59,7 @@ have_func("&rb_process_status_new")
|
|
|
54
59
|
$defs << "-DUM_KERNEL_VERSION=#{config[:kernel_version]}"
|
|
55
60
|
$defs << '-DHAVE_IO_URING_PREP_BIND' if config[:prep_bind]
|
|
56
61
|
$defs << '-DHAVE_IO_URING_PREP_LISTEN' if config[:prep_listen]
|
|
62
|
+
$defs << '-DHAVE_IO_URING_SEND_VECTORIZED' if config[:send_vectoized]
|
|
57
63
|
|
|
58
64
|
$CFLAGS << ' -Wno-pointer-arith'
|
|
59
65
|
|