uringmachine 1.0.0 → 1.0.1
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 +4 -0
- data/benchmark/bm_net_http.rb +54 -0
- data/benchmark/bm_net_http_support.rb +109 -0
- data/benchmark/common.rb +2 -2
- data/lib/uringmachine/fiber_scheduler.rb +12 -4
- data/lib/uringmachine/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6c4607791ec8b3aefe794167dc5a0ec500fe4ec4a58b56962d90d27fdd4c41a7
|
|
4
|
+
data.tar.gz: 151205d0887aef4cc0cf022baf295b9be8934f2bb895fd87bd98680a837349a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4f580578fabf57dbc7c9c8d609a000b4b2ebc56ffb1273e3496137b7ba01f4ca71767e37d84d978dbf49b5012a211374cceb774b787f748c4b3351531a42c42
|
|
7
|
+
data.tar.gz: 0cc8a8d93345894b2f2b3570390ae829dea4bac1e975a0d777c7359e74161273df9658b0797bd2ecbe45a262efbd7588dd8f9ec673577d91e7e8030bd9ab15e4
|
data/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative './common'
|
|
4
|
+
require_relative './bm_net_http_support'
|
|
5
|
+
|
|
6
|
+
require 'net/http'
|
|
7
|
+
|
|
8
|
+
CONCURRENCY = ENV['C']&.to_i || 50
|
|
9
|
+
ITERATIONS = ENV['I']&.to_i || 200
|
|
10
|
+
|
|
11
|
+
# Adapted from benchmark code in https://github.com/yaroslav/carbon_fiber
|
|
12
|
+
|
|
13
|
+
RESPONSE_BODY = %({"ok":true,"value":12345})
|
|
14
|
+
REQUEST_PATH = "/api"
|
|
15
|
+
|
|
16
|
+
SERVER = LoopbackServer.new(
|
|
17
|
+
response_body: RESPONSE_BODY,
|
|
18
|
+
content_type: "application/json"
|
|
19
|
+
)
|
|
20
|
+
PORT = SERVER.port
|
|
21
|
+
sleep(0.1)
|
|
22
|
+
|
|
23
|
+
class UMBenchmark
|
|
24
|
+
def run_http_client
|
|
25
|
+
Net::HTTP.start("127.0.0.1", PORT, nil, nil) do |http|
|
|
26
|
+
ITERATIONS.times do
|
|
27
|
+
request = Net::HTTP::Get.new(REQUEST_PATH)
|
|
28
|
+
response = http.request(request)
|
|
29
|
+
raise "unexpected status #{response.code}" unless response.code == "200"
|
|
30
|
+
raise "unexpected body size" unless response.body&.bytesize == RESPONSE_BODY.bytesize
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
rescue => e
|
|
34
|
+
p e
|
|
35
|
+
p e.backtrace
|
|
36
|
+
exit!
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def do_threads(threads, ios)
|
|
40
|
+
CONCURRENCY.times do
|
|
41
|
+
threads << Thread.new { run_http_client }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def do_scheduler(scheduler, ios)
|
|
46
|
+
CONCURRENCY.times do
|
|
47
|
+
Fiber.schedule { run_http_client }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def do_scheduler_x(div, scheduler, ios)
|
|
52
|
+
(CONCURRENCY/div).times { run_http_client }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from benchmark code in https://github.com/yaroslav/carbon_fiber
|
|
4
|
+
|
|
5
|
+
require "socket"
|
|
6
|
+
|
|
7
|
+
class LoopbackServer
|
|
8
|
+
attr_reader :port
|
|
9
|
+
|
|
10
|
+
def initialize(response_body:, content_type:)
|
|
11
|
+
@response_body = response_body.b
|
|
12
|
+
@content_type = content_type
|
|
13
|
+
@response = build_response(@response_body, @content_type)
|
|
14
|
+
@server = TCPServer.new("127.0.0.1", 0)
|
|
15
|
+
@port = @server.local_address.ip_port
|
|
16
|
+
@closed = false
|
|
17
|
+
@lock = Thread::Mutex.new
|
|
18
|
+
@client_threads = []
|
|
19
|
+
@client_sockets = []
|
|
20
|
+
@accept_thread = Thread.new { accept_loop }
|
|
21
|
+
@accept_thread.report_on_exception = false
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def close
|
|
25
|
+
return if @closed
|
|
26
|
+
|
|
27
|
+
if Fiber.scheduler
|
|
28
|
+
closer = Thread.new { close_without_scheduler }
|
|
29
|
+
closer.report_on_exception = false
|
|
30
|
+
closer.join(2.0)
|
|
31
|
+
else
|
|
32
|
+
close_without_scheduler
|
|
33
|
+
end
|
|
34
|
+
rescue
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def close_without_scheduler
|
|
40
|
+
return if @closed
|
|
41
|
+
|
|
42
|
+
@closed = true
|
|
43
|
+
@server.close unless @server.closed?
|
|
44
|
+
@lock.synchronize do
|
|
45
|
+
@client_sockets.each do |socket|
|
|
46
|
+
socket.close unless socket.closed?
|
|
47
|
+
rescue
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
@accept_thread.join(0.5)
|
|
51
|
+
@lock.synchronize { @client_threads.dup }.each { |thread| thread.join(0.5) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def accept_loop
|
|
55
|
+
loop do
|
|
56
|
+
conn = @server.accept
|
|
57
|
+
conn.sync = true
|
|
58
|
+
|
|
59
|
+
worker = Thread.new(conn) do |socket|
|
|
60
|
+
Thread.current.report_on_exception = false
|
|
61
|
+
handle_client(socket)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
@lock.synchronize do
|
|
65
|
+
@client_sockets << conn
|
|
66
|
+
@client_threads << worker
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
rescue IOError, Errno::EBADF
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def handle_client(socket)
|
|
74
|
+
loop do
|
|
75
|
+
request = read_request(socket)
|
|
76
|
+
break unless request
|
|
77
|
+
|
|
78
|
+
socket.write(@response)
|
|
79
|
+
end
|
|
80
|
+
rescue IOError, Errno::ECONNRESET, Errno::EPIPE
|
|
81
|
+
nil
|
|
82
|
+
ensure
|
|
83
|
+
begin
|
|
84
|
+
@lock.synchronize { @client_sockets.delete(socket) }
|
|
85
|
+
socket.close unless socket.closed?
|
|
86
|
+
rescue # rubocop:disable Lint/SuppressedException
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def read_request(socket)
|
|
91
|
+
buffer = +""
|
|
92
|
+
loop do
|
|
93
|
+
buffer << socket.readpartial(4096)
|
|
94
|
+
break if buffer.include?("\r\n\r\n")
|
|
95
|
+
end
|
|
96
|
+
buffer
|
|
97
|
+
rescue IOError, Errno::ECONNRESET, Errno::EPIPE
|
|
98
|
+
nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def build_response(body, content_type)
|
|
102
|
+
"HTTP/1.1 200 OK\r\n" \
|
|
103
|
+
"Content-Length: #{body.bytesize}\r\n" \
|
|
104
|
+
"Content-Type: #{content_type}\r\n" \
|
|
105
|
+
"Connection: keep-alive\r\n" \
|
|
106
|
+
"\r\n" \
|
|
107
|
+
"#{body}"
|
|
108
|
+
end
|
|
109
|
+
end
|
data/benchmark/common.rb
CHANGED
|
@@ -64,14 +64,14 @@ class UMBenchmark
|
|
|
64
64
|
|
|
65
65
|
threads: [:threads, "Threads"],
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
async_uring: [:scheduler, "Async uring"],
|
|
68
68
|
# async_uring_x2: [:scheduler_x, "Async uring x2"],
|
|
69
69
|
|
|
70
70
|
# async_epoll: [:scheduler, "Async epoll"],
|
|
71
71
|
# async_epoll_x2: [:scheduler_x, "Async epoll x2"],
|
|
72
72
|
|
|
73
73
|
um_fs: [:scheduler, "UM FS"],
|
|
74
|
-
um_fs_x2: [:scheduler_x, "UM FS x2"],
|
|
74
|
+
# um_fs_x2: [:scheduler_x, "UM FS x2"],
|
|
75
75
|
|
|
76
76
|
um: [:um, "UM"],
|
|
77
77
|
# um_sidecar: [:um, "UM sidecar"],
|
|
@@ -210,13 +210,21 @@ class UringMachine
|
|
|
210
210
|
# Waits for the given io to become ready.
|
|
211
211
|
#
|
|
212
212
|
# @param io [IO] IO object
|
|
213
|
-
# @param events [
|
|
213
|
+
# @param events [Integer] readiness bitmask
|
|
214
214
|
# @param timeout [Number, nil] optional timeout
|
|
215
|
-
# @return [
|
|
215
|
+
# @return [Integer] ready events bitmask
|
|
216
216
|
def io_wait(io, events, timeout = nil)
|
|
217
|
-
#
|
|
217
|
+
# Useful note from the Carbon Fiber Fiber::Scheduler implementation:
|
|
218
|
+
# Net::HTTP#begin_transport calls `wait_readable(0)` before every
|
|
219
|
+
# keep-alive request to probe for a closed connection. On a healthy
|
|
220
|
+
# connection this is always "not readable", so returning false
|
|
221
|
+
# directly saves one MSG_PEEK recvfrom per request. On a genuinely
|
|
222
|
+
# closed connection Net::HTTP will detect EOF on the next real read
|
|
223
|
+
# and reconnect — one extra request's worth of latency, at most.
|
|
224
|
+
return 0 if timeout == 0 && events == ::IO::READABLE && io.is_a?(BasicSocket)
|
|
225
|
+
|
|
218
226
|
timeout ||= io.timeout
|
|
219
|
-
if timeout
|
|
227
|
+
if timeout && timeout > 0
|
|
220
228
|
@machine.timeout(timeout, Timeout::Error) {
|
|
221
229
|
@machine.poll(io.fileno, events)
|
|
222
230
|
}
|
data/lib/uringmachine/version.rb
CHANGED
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: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sharon Rosner
|
|
@@ -61,6 +61,8 @@ files:
|
|
|
61
61
|
- benchmark/bm_io_ssl.rb
|
|
62
62
|
- benchmark/bm_mutex_cpu.rb
|
|
63
63
|
- benchmark/bm_mutex_io.rb
|
|
64
|
+
- benchmark/bm_net_http.rb
|
|
65
|
+
- benchmark/bm_net_http_support.rb
|
|
64
66
|
- benchmark/bm_pg_client.rb
|
|
65
67
|
- benchmark/bm_queue.rb
|
|
66
68
|
- benchmark/bm_redis_client.rb
|