evt 0.3.0 → 0.3.5
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/README.md +13 -12
- data/evt.gemspec +1 -0
- data/ext/evt/epoll.h +9 -8
- data/ext/evt/evt.h +0 -2
- data/ext/evt/select.h +11 -8
- data/ext/evt/uring.h +16 -26
- data/lib/evt/backends/bundled.rb +34 -7
- data/lib/evt/backends/iocp.rb +0 -1
- data/lib/evt/backends/kqueue.rb +0 -1
- data/lib/evt/backends/select.rb +3 -1
- data/lib/evt/backends/uring.rb +1 -2
- data/lib/evt/scheduler.rb +1 -1
- data/lib/evt/version.rb +1 -1
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a62836e0e4fd3eb50837a3112e54c0ee723eac5c7d732db6d4be05e0e2c63989
|
4
|
+
data.tar.gz: 1a10b744a967d71094fdc34e4678a23339c48997ca57c30b46e1e7a848de74bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bbc6ab2e1e30cc456d29206cac4fb05b13e83415451880b1155024b052d0fb4e0c2abc90a928e5562a0dd926ea9ad67e11f3c9b2e0082fa5cd9918e5b099247
|
7
|
+
data.tar.gz: a4a76eafa9f45ffb9e8c886568bc26c213c02e36283d31b54badc7d80b561ab076c476301b344cd78e5f0c7ba5fd8d543169829cf23ab4119fe69461b29e9f20
|
data/README.md
CHANGED
@@ -15,33 +15,34 @@ The Event Library that designed for Ruby 3.0 Fiber Scheduler.
|
|
15
15
|
|
16
16
|
| | Linux | Windows | macOS | FreeBSD |
|
17
17
|
| --------------- | ----------- | ------------| ----------- | ----------- |
|
18
|
-
| io_uring |
|
18
|
+
| io_uring | ⚠️ (See 1) | ❌ | ❌ | ❌ |
|
19
19
|
| epoll | ✅ (See 2) | ❌ | ❌ | ❌ |
|
20
20
|
| kqueue | ❌ | ❌ | ✅ (⚠️ See 5) | ✅ |
|
21
21
|
| IOCP | ❌ | ❌ (⚠️See 3) | ❌ | ❌ |
|
22
22
|
| Ruby (`IO.select`) | ✅ Fallback | ✅ (⚠️See 4) | ✅ Fallback | ✅ Fallback |
|
23
23
|
|
24
|
-
1. when liburing is installed
|
25
|
-
2. when kernel version >= 2.6.
|
24
|
+
1. when liburing is installed. (Currently fixing)
|
25
|
+
2. when kernel version >= 2.6.9
|
26
26
|
3. WOULD NOT WORK until `FILE_FLAG_OVERLAPPED` is included in I/O initialization process.
|
27
27
|
4. Some I/Os are not able to be nonblock under Windows. See [Scheduler Docs](https://docs.ruby-lang.org/en/master/doc/scheduler_md.html#label-IO).
|
28
28
|
5. `kqueue` performance in Darwin is very poor. **MAY BE DISABLED IN THE FUTURE.**
|
29
29
|
|
30
30
|
### Benchmark
|
31
31
|
|
32
|
-
The benchmark is running under `v0.
|
32
|
+
The benchmark is running under `v0.3.1` version. See `example.rb` in [midori](https://github.com/midori-rb/midori.rb) for test code, the test is running under a single-thread server.
|
33
33
|
|
34
34
|
The test command is `wrk -t4 -c8192 -d30s http://localhost:3001`.
|
35
35
|
|
36
36
|
All of the systems have set their file descriptor limit to maximum.
|
37
|
-
|
38
|
-
|
39
|
-
|
|
40
|
-
|
|
41
|
-
| Linux | Ryzen 2700x | 64GB |
|
42
|
-
| Linux | Ryzen 2700x | 64GB |
|
43
|
-
|
|
44
|
-
| macOS | i7-6820HQ | 16GB |
|
37
|
+
On systems raising "Fiber unable to allocate memory", `sudo sysctl -w vm.max_map_count=1000000` is set.
|
38
|
+
|
39
|
+
| OS | CPU | Memory | Backend | req/s |
|
40
|
+
| ----- | ----------- | ------ | ---------------------- | --------------|
|
41
|
+
| Linux | Ryzen 2700x | 64GB | epoll | 1853259.47 |
|
42
|
+
| Linux | Ryzen 2700x | 64GB | io_uring | require fixes |
|
43
|
+
| Linux | Ryzen 2700x | 64GB | IO.select (using poll) | 1636849.15 |
|
44
|
+
| macOS | i7-6820HQ | 16GB | kqueue | 247370.37 |
|
45
|
+
| macOS | i7-6820HQ | 16GB | IO.select (using poll) | 323391.38 |
|
45
46
|
|
46
47
|
## Install
|
47
48
|
|
data/evt.gemspec
CHANGED
data/ext/evt/epoll.h
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
#include "evt.h"
|
4
4
|
|
5
5
|
#if HAVE_SYS_EPOLL_H
|
6
|
+
|
6
7
|
VALUE method_scheduler_epoll_init(VALUE self) {
|
7
8
|
rb_iv_set(self, "@epfd", INT2NUM(epoll_create(1))); // Size of epoll is ignored after Linux 2.6.8.
|
8
9
|
return Qnil;
|
@@ -31,14 +32,6 @@ VALUE method_scheduler_epoll_register(VALUE self, VALUE io, VALUE interest) {
|
|
31
32
|
return Qnil;
|
32
33
|
}
|
33
34
|
|
34
|
-
VALUE method_scheduler_epoll_deregister(VALUE self, VALUE io) {
|
35
|
-
ID id_fileno = rb_intern("fileno");
|
36
|
-
int epfd = NUM2INT(rb_iv_get(self, "@epfd"));
|
37
|
-
int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
|
38
|
-
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); // Require Linux 2.6.9 for NULL event.
|
39
|
-
return Qnil;
|
40
|
-
}
|
41
|
-
|
42
35
|
VALUE method_scheduler_epoll_wait(VALUE self) {
|
43
36
|
int n, epfd, i, event_flag, timeout;
|
44
37
|
VALUE next_timeout, obj_io, readables, writables, result;
|
@@ -82,6 +75,14 @@ VALUE method_scheduler_epoll_wait(VALUE self) {
|
|
82
75
|
return result;
|
83
76
|
}
|
84
77
|
|
78
|
+
VALUE method_scheduler_epoll_deregister(VALUE self, VALUE io) {
|
79
|
+
ID id_fileno = rb_intern("fileno");
|
80
|
+
int epfd = NUM2INT(rb_iv_get(self, "@epfd"));
|
81
|
+
int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
|
82
|
+
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); // Require Linux 2.6.9 for NULL event.
|
83
|
+
return Qnil;
|
84
|
+
}
|
85
|
+
|
85
86
|
VALUE method_scheduler_epoll_backend(VALUE klass) {
|
86
87
|
return rb_str_new_cstr("epoll");
|
87
88
|
}
|
data/ext/evt/evt.h
CHANGED
@@ -13,7 +13,6 @@ void Init_evt_ext();
|
|
13
13
|
#if HAVE_LIBURING_H
|
14
14
|
VALUE method_scheduler_uring_init(VALUE self);
|
15
15
|
VALUE method_scheduler_uring_register(VALUE self, VALUE io, VALUE interest);
|
16
|
-
VALUE method_scheduler_uring_deregister(VALUE self, VALUE io);
|
17
16
|
VALUE method_scheduler_uring_wait(VALUE self);
|
18
17
|
VALUE method_scheduler_uring_backend(VALUE klass);
|
19
18
|
VALUE method_scheduler_uring_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length);
|
@@ -55,7 +54,6 @@ void Init_evt_ext();
|
|
55
54
|
#if HAVE_SYS_EVENT_H
|
56
55
|
VALUE method_scheduler_kqueue_init(VALUE self);
|
57
56
|
VALUE method_scheduler_kqueue_register(VALUE self, VALUE io, VALUE interest);
|
58
|
-
VALUE method_scheduler_kqueue_deregister(VALUE self, VALUE io);
|
59
57
|
VALUE method_scheduler_kqueue_wait(VALUE self);
|
60
58
|
VALUE method_scheduler_kqueue_backend(VALUE klass);
|
61
59
|
#include <sys/event.h>
|
data/ext/evt/select.h
CHANGED
@@ -3,19 +3,22 @@
|
|
3
3
|
#include "evt.h"
|
4
4
|
|
5
5
|
VALUE method_scheduler_select_wait(VALUE self) {
|
6
|
-
// return IO.select(@readable.keys, @writable.keys, [], next_timeout)
|
7
|
-
VALUE readable, writable, readable_keys, writable_keys, next_timeout;
|
6
|
+
// return IO.select(@readable.keys, @writable.keys, [], next_timeout / 1000.0)
|
8
7
|
ID id_select = rb_intern("select");
|
9
8
|
ID id_next_timeout = rb_intern("next_timeout");
|
9
|
+
ID id_div = rb_intern("/");
|
10
|
+
ID id_to_f = rb_intern("to_f");
|
10
11
|
|
11
|
-
readable = rb_iv_get(self, "@readable");
|
12
|
-
writable = rb_iv_get(self, "@writable");
|
12
|
+
VALUE readable = rb_iv_get(self, "@readable");
|
13
|
+
VALUE writable = rb_iv_get(self, "@writable");
|
13
14
|
|
14
|
-
readable_keys = rb_funcall(readable, rb_intern("keys"), 0);
|
15
|
-
writable_keys = rb_funcall(writable, rb_intern("keys"), 0);
|
16
|
-
next_timeout = rb_funcall(self, id_next_timeout, 0);
|
15
|
+
VALUE readable_keys = rb_funcall(readable, rb_intern("keys"), 0);
|
16
|
+
VALUE writable_keys = rb_funcall(writable, rb_intern("keys"), 0);
|
17
|
+
VALUE next_timeout = rb_funcall(self, id_next_timeout, 0);
|
18
|
+
next_timeout = rb_funcall(next_timeout, id_to_f, 0);
|
19
|
+
VALUE secs = rb_funcall(next_timeout, id_div, 1, DBL2NUM(1000.0));
|
17
20
|
|
18
|
-
return rb_funcall(rb_cIO, id_select, 4, readable_keys, writable_keys, rb_ary_new(),
|
21
|
+
return rb_funcall(rb_cIO, id_select, 4, readable_keys, writable_keys, rb_ary_new(), secs);
|
19
22
|
}
|
20
23
|
|
21
24
|
VALUE method_scheduler_select_backend(VALUE klass) {
|
data/ext/evt/uring.h
CHANGED
@@ -79,6 +79,12 @@ VALUE method_scheduler_uring_wait(VALUE self) {
|
|
79
79
|
iovs = rb_ary_new();
|
80
80
|
|
81
81
|
TypedData_Get_Struct(rb_iv_get(self, "@ring"), struct io_uring, &type_uring_payload, ring);
|
82
|
+
|
83
|
+
struct __kernel_timespec ts;
|
84
|
+
ts.tv_sec = NUM2INT(next_timeout);
|
85
|
+
ts.tv_nsec = 0;
|
86
|
+
|
87
|
+
io_uring_wait_cqe_timeout(ring, cqes, &ts);
|
82
88
|
ret = io_uring_peek_batch_cqe(ring, cqes, URING_MAX_EVENTS);
|
83
89
|
|
84
90
|
for (i = 0; i < ret; i++) {
|
@@ -94,21 +100,14 @@ VALUE method_scheduler_uring_wait(VALUE self) {
|
|
94
100
|
rb_funcall(writables, id_push, 1, obj_io);
|
95
101
|
}
|
96
102
|
} else {
|
97
|
-
|
103
|
+
VALUE v = rb_ary_new2(2);
|
104
|
+
rb_ary_store(v, 0, obj_io);
|
105
|
+
rb_ary_store(v, 1, obj_io);
|
106
|
+
rb_funcall(iovs, id_push, 1, SIZET2NUM(cqes[i]->res));
|
98
107
|
}
|
99
108
|
io_uring_cqe_seen(ring, cqes[i]);
|
100
109
|
}
|
101
110
|
|
102
|
-
if (ret == 0) {
|
103
|
-
if (next_timeout != Qnil && NUM2INT(next_timeout) != -1) {
|
104
|
-
// sleep
|
105
|
-
time = next_timeout / 1000;
|
106
|
-
rb_funcall(rb_mKernel, id_sleep, 1, rb_float_new(time));
|
107
|
-
} else {
|
108
|
-
rb_funcall(rb_mKernel, id_sleep, 1, rb_float_new(0.001)); // To avoid infinite loop
|
109
|
-
}
|
110
|
-
}
|
111
|
-
|
112
111
|
result = rb_ary_new2(3);
|
113
112
|
rb_ary_store(result, 0, readables);
|
114
113
|
rb_ary_store(result, 1, writables);
|
@@ -132,27 +131,23 @@ VALUE method_scheduler_uring_io_read(VALUE self, VALUE io, VALUE buffer, VALUE o
|
|
132
131
|
int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
|
133
132
|
|
134
133
|
read_buffer = (char*) xmalloc(NUM2SIZET(length));
|
135
|
-
struct iovec iov = {
|
136
|
-
.iov_base = read_buffer,
|
137
|
-
.iov_len = NUM2SIZET(length),
|
138
|
-
};
|
139
134
|
|
140
135
|
data = (struct uring_data*) xmalloc(sizeof(struct uring_data));
|
141
136
|
data->is_poll = false;
|
142
137
|
data->io = io;
|
143
138
|
data->poll_mask = 0;
|
144
139
|
|
145
|
-
|
140
|
+
io_uring_prep_read(sqe, fd, read_buffer, 1, NUM2SIZET(length), NUM2SIZET(offset));
|
146
141
|
io_uring_sqe_set_data(sqe, data);
|
147
142
|
io_uring_submit(ring);
|
148
143
|
|
144
|
+
VALUE ret = rb_funcall(Fiber, rb_intern("yield"), 0); // Fiber.yield
|
145
|
+
|
149
146
|
VALUE result = rb_str_new(read_buffer, strlen(read_buffer));
|
150
147
|
if (buffer != Qnil) {
|
151
148
|
rb_str_append(buffer, result);
|
152
149
|
}
|
153
|
-
|
154
|
-
rb_funcall(Fiber, rb_intern("yield"), 0); // Fiber.yield
|
155
|
-
return result;
|
150
|
+
return ret;
|
156
151
|
}
|
157
152
|
|
158
153
|
VALUE method_scheduler_uring_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
|
@@ -170,21 +165,16 @@ VALUE method_scheduler_uring_io_write(VALUE self, VALUE io, VALUE buffer, VALUE
|
|
170
165
|
int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
|
171
166
|
|
172
167
|
write_buffer = StringValueCStr(buffer);
|
173
|
-
struct iovec iov = {
|
174
|
-
.iov_base = write_buffer,
|
175
|
-
.iov_len = NUM2SIZET(length),
|
176
|
-
};
|
177
168
|
|
178
169
|
data = (struct uring_data*) xmalloc(sizeof(struct uring_data));
|
179
170
|
data->is_poll = false;
|
180
171
|
data->io = io;
|
181
172
|
data->poll_mask = 0;
|
182
173
|
|
183
|
-
|
174
|
+
io_uring_prep_write(sqe, fd, write_buffer, NUM2SIZET(length), NUM2SIZET(offset));
|
184
175
|
io_uring_sqe_set_data(sqe, data);
|
185
176
|
io_uring_submit(ring);
|
186
|
-
rb_funcall(Fiber, rb_intern("yield"), 0);
|
187
|
-
return length;
|
177
|
+
return rb_funcall(Fiber, rb_intern("yield"), 0);
|
188
178
|
}
|
189
179
|
|
190
180
|
VALUE method_scheduler_uring_backend(VALUE klass) {
|
data/lib/evt/backends/bundled.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Evt::Bundled
|
4
|
-
MAXIMUM_TIMEOUT =
|
4
|
+
MAXIMUM_TIMEOUT = 5000
|
5
|
+
COLLECT_COUNTER_MAX = 16384
|
5
6
|
|
6
7
|
def initialize
|
7
8
|
@readable = {}
|
@@ -12,6 +13,7 @@ class Evt::Bundled
|
|
12
13
|
@lock = Mutex.new
|
13
14
|
@blocking = 0
|
14
15
|
@ready = []
|
16
|
+
@collect_counter = 0
|
15
17
|
|
16
18
|
init_selector
|
17
19
|
end
|
@@ -21,10 +23,10 @@ class Evt::Bundled
|
|
21
23
|
attr_reader :waiting
|
22
24
|
|
23
25
|
def next_timeout
|
24
|
-
_fiber, timeout = @waiting.min_by{|key, value| value}
|
26
|
+
_fiber, timeout = @waiting.min_by{ |key, value| value }
|
25
27
|
|
26
28
|
if timeout
|
27
|
-
offset = timeout - current_time
|
29
|
+
offset = (timeout - current_time) * 1000 # Use mililisecond
|
28
30
|
return 0 if offset < 0
|
29
31
|
return offset if offset < MAXIMUM_TIMEOUT
|
30
32
|
end
|
@@ -47,19 +49,22 @@ class Evt::Bundled
|
|
47
49
|
end
|
48
50
|
|
49
51
|
unless iovs.nil?
|
50
|
-
iovs&.each do |
|
52
|
+
iovs&.each do |v|
|
53
|
+
io, ret = v
|
51
54
|
fiber = @iovs.delete(io)
|
52
|
-
fiber&.resume
|
55
|
+
fiber&.resume(ret)
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
59
|
+
collect
|
60
|
+
|
56
61
|
if @waiting.any?
|
57
62
|
time = current_time
|
58
63
|
waiting, @waiting = @waiting, {}
|
59
64
|
|
60
65
|
waiting.each do |fiber, timeout|
|
61
66
|
if timeout <= time
|
62
|
-
fiber.resume
|
67
|
+
fiber.resume if fiber.is_a? Fiber and fiber.alive?
|
63
68
|
else
|
64
69
|
@waiting[fiber] = timeout
|
65
70
|
end
|
@@ -74,7 +79,7 @@ class Evt::Bundled
|
|
74
79
|
end
|
75
80
|
|
76
81
|
ready.each do |fiber|
|
77
|
-
fiber.resume
|
82
|
+
fiber.resume if fiber.is_a? Fiber and fiber.alive?
|
78
83
|
end
|
79
84
|
end
|
80
85
|
end
|
@@ -145,6 +150,28 @@ class Evt::Bundled
|
|
145
150
|
self.run
|
146
151
|
end
|
147
152
|
|
153
|
+
# Collect closed streams in readables and writables
|
154
|
+
def collect(force=false)
|
155
|
+
if @collect_counter < COLLECT_COUNTER_MAX and !force
|
156
|
+
@collect_counter += 1
|
157
|
+
return
|
158
|
+
end
|
159
|
+
|
160
|
+
@collect_counter = 0
|
161
|
+
|
162
|
+
@readable.keys.each do |io|
|
163
|
+
@readable.delete(io) if io.closed?
|
164
|
+
end
|
165
|
+
|
166
|
+
@writable.keys.each do |io|
|
167
|
+
@writable.delete(io) if io.closed?
|
168
|
+
end
|
169
|
+
|
170
|
+
@iovs.keys.each do |io|
|
171
|
+
@iovs.delete(io) if io.closed?
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
148
175
|
# Intercept the creation of a non-blocking fiber.
|
149
176
|
# @returns [Fiber]
|
150
177
|
def fiber(&block)
|
data/lib/evt/backends/iocp.rb
CHANGED
data/lib/evt/backends/kqueue.rb
CHANGED
data/lib/evt/backends/select.rb
CHANGED
data/lib/evt/backends/uring.rb
CHANGED
@@ -14,11 +14,10 @@ class Evt::Uring < Evt::Bundled
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def register(io, interest)
|
17
|
-
uring_register(io,
|
17
|
+
uring_register(io, interest)
|
18
18
|
end
|
19
19
|
|
20
20
|
def deregister(io)
|
21
|
-
# io_uring running under one-shot mode, no need to deregister
|
22
21
|
end
|
23
22
|
|
24
23
|
def io_read(io, buffer, offset, length)
|
data/lib/evt/scheduler.rb
CHANGED
data/lib/evt/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: evt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delton Ding
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-12-
|
11
|
+
date: 2020-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.20.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest-reporters
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.4'
|
41
55
|
description: A low-level Event Handler designed for Ruby 3 Scheduler for better performance
|
42
56
|
email:
|
43
57
|
- dsh0416@gmail.com
|
@@ -78,7 +92,7 @@ licenses:
|
|
78
92
|
metadata:
|
79
93
|
homepage_uri: https://github.com/dsh0416/evt
|
80
94
|
source_code_uri: https://github.com/dsh0416/evt
|
81
|
-
post_install_message:
|
95
|
+
post_install_message:
|
82
96
|
rdoc_options: []
|
83
97
|
require_paths:
|
84
98
|
- lib
|
@@ -94,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
108
|
version: '0'
|
95
109
|
requirements: []
|
96
110
|
rubygems_version: 3.2.2
|
97
|
-
signing_key:
|
111
|
+
signing_key:
|
98
112
|
specification_version: 4
|
99
113
|
summary: The Event Library that designed for Ruby 3.0 Fiber Scheluer.
|
100
114
|
test_files: []
|