ffi-rzmq 0.9.0 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +3 -0
- data/History.txt +31 -0
- data/README.rdoc +21 -11
- data/Rakefile +2 -34
- data/examples/README.rdoc +1 -1
- data/examples/v2api/latency_measurement.rb +139 -0
- data/examples/v2api/xreqxrep_poll.rb +93 -0
- data/examples/v3api/latency_measurement.rb +139 -0
- data/examples/v3api/xreqxrep_poll.rb +93 -0
- data/ffi-rzmq.gemspec +17 -26
- data/lib/ffi-rzmq/constants.rb +4 -37
- data/lib/ffi-rzmq/libzmq.rb +174 -161
- data/lib/ffi-rzmq/poll.rb +27 -10
- data/lib/ffi-rzmq/socket.rb +116 -85
- data/lib/ffi-rzmq/util.rb +22 -1
- data/lib/ffi-rzmq/version.rb +3 -0
- data/spec/nonblocking_recv_spec.rb +46 -40
- data/spec/poll_spec.rb +84 -0
- data/spec/pushpull_spec.rb +35 -2
- data/spec/socket_spec.rb +0 -2
- data/spec/spec_helper.rb +3 -7
- metadata +117 -95
- data/version.txt +0 -1
data/.gitignore
ADDED
data/Gemfile
ADDED
data/History.txt
CHANGED
@@ -1,3 +1,34 @@
|
|
1
|
+
== 0.9.2 / 20111115
|
2
|
+
* Removed all references to the version4 API.
|
3
|
+
|
4
|
+
* Dropped support for 3.0.x and added support for 3.1.x. The 0mq
|
5
|
+
community has pretty much voted to abandon the path taken in 3.0
|
6
|
+
so the 3.1 branch is the API that will be supported.
|
7
|
+
|
8
|
+
* Fixed a bug in Poller#delete where it would erroneously return
|
9
|
+
false even when it successfully deleted a socket. Issue 46.
|
10
|
+
|
11
|
+
* All specs pass for 2.1.x API.
|
12
|
+
|
13
|
+
* 3 specs fail when run with 3.1 API; these are due to bugs in the
|
14
|
+
0mq library and are *not* ffi-rzmq bugs.
|
15
|
+
|
16
|
+
* Rescue LoadErrors when loading libzmq. Print a warning about
|
17
|
+
adding libzmq.dll to the Windows PATH for that platform. Print
|
18
|
+
the search paths where the gem looks for libzmq.
|
19
|
+
|
20
|
+
== 0.9.1 / 20111027
|
21
|
+
* Moved LibC and LibZMQ into the ZMQ module namespace. Necessary to
|
22
|
+
avoid namespace collisions with other libraries that also use
|
23
|
+
the constants LibC and/or LibZMQ.
|
24
|
+
|
25
|
+
* Fixed a bug where file descriptors registered on Poll were never
|
26
|
+
returned as readable or writable.
|
27
|
+
|
28
|
+
* Added Socket#recv_multipart. This returns the message body and
|
29
|
+
return address envelope as separate arrays. Only to be used with
|
30
|
+
XREQ/XREP/DEALER/ROUTER sockets.
|
31
|
+
|
1
32
|
== 0.9.0 / 20110930
|
2
33
|
* Changed the behavior of every method that used to produce exceptions.
|
3
34
|
The methods now behave more like the C API functions. They return
|
data/README.rdoc
CHANGED
@@ -9,10 +9,11 @@ function interface). It's a pure ruby wrapper so this gem can be loaded
|
|
9
9
|
and run by any ruby runtime that supports FFI. That's all of them:
|
10
10
|
MRI 1.9.x, Rubinius and JRuby.
|
11
11
|
|
12
|
-
This single gem supports 0mq 2.x and 3.x 0mq APIs. The 0mq project started
|
13
|
-
making backward-incompatible changes to the API with the 3.x release.
|
12
|
+
This single gem supports 0mq 2.x and 3.1.x 0mq APIs. The 0mq project started
|
13
|
+
making backward-incompatible changes to the API with the 3.1.x release.
|
14
14
|
The gem auto-configures itself to expose the API conforming to the loaded
|
15
|
-
C library.
|
15
|
+
C library. 0mq API 3.0 is *not* supported; the 0mq community voted to
|
16
|
+
abandon it.
|
16
17
|
|
17
18
|
The impetus behind this library was to provide support for ZeroMQ in
|
18
19
|
JRuby which has native threads. Unlike MRI, which has a GIL, JRuby and
|
@@ -25,9 +26,12 @@ JRuby and Rubinius will likely be the best environments to run this library.
|
|
25
26
|
There has been a major change in API from the 0.8.x series to 0.9.0.
|
26
27
|
Previously the Socket#send and Socket#recv methods could raise exceptions
|
27
28
|
when there was an error. They also returned true or false depending on
|
28
|
-
the success
|
29
|
-
|
30
|
-
|
29
|
+
the success or failure of the operation. The exceptions were also used
|
30
|
+
for managing flow control when doing non-blocking send/recv.
|
31
|
+
|
32
|
+
Mixing and matching return codes and exceptions is a terrible idea in
|
33
|
+
practice. Using them for flow control is an even worse sin. So all of
|
34
|
+
the exception code from Socket has been removed.
|
31
35
|
|
32
36
|
The Poller and Message class have also had their exceptions removed for
|
33
37
|
returning errors on instance methods.
|
@@ -42,9 +46,11 @@ constructed object or nil. Code should check for nil returns before using
|
|
42
46
|
the object.
|
43
47
|
|
44
48
|
This is a *breaking* change. I'm sorry but the old API's inconsistency was
|
45
|
-
causing too many problems. It now more closely mimics the 0mq C API.
|
46
|
-
|
47
|
-
|
49
|
+
causing too many problems. It now more closely mimics the 0mq C API. IF YOU
|
50
|
+
PREFER THE ORIGINAL API WHICH WAS CLOSER TO IDIOMATIC RUBY, IT SHOULD BE
|
51
|
+
EASY TO WRAP THESE CLASSES UP TO PROVIDE A MORE IDIOMATIC API. I SUGGEST
|
52
|
+
YOU CREATE A PATCH *OR* CREATE A SEPARATE GEM TO WRAP THIS ONE. Sorry for
|
53
|
+
shouting but I wanted to make sure this stood out.
|
48
54
|
|
49
55
|
All example code has been updated to use the new Ruby API.
|
50
56
|
|
@@ -135,7 +141,7 @@ I highly recommend visiting the Learn Ruby 0mq project for a bunch of good code
|
|
135
141
|
|
136
142
|
== REQUIREMENTS:
|
137
143
|
|
138
|
-
* 0mq 2.1.x or later; 2.0.x
|
144
|
+
* 0mq 2.1.x, 3.1.x or later; 2.0.x and 3.0.x are no longer supported
|
139
145
|
|
140
146
|
The ZeroMQ library must be installed on your system in a well-known location
|
141
147
|
like /usr/local/lib. This is the default for new ZeroMQ installs.
|
@@ -167,7 +173,11 @@ To build from git master:
|
|
167
173
|
% gem build ffi-rzmq.gemspec
|
168
174
|
% gem install ffi-rzmq-*.gem
|
169
175
|
|
170
|
-
|
176
|
+
|
177
|
+
NOTE for Windows users!
|
178
|
+
In order for this gem to find the libzmq.dll, it *must* be on the Windows PATH. Google
|
179
|
+
for "modify windows path" for instructions on how to do that if you are unfamiliar with
|
180
|
+
that activity.
|
171
181
|
|
172
182
|
== LICENSE:
|
173
183
|
|
data/Rakefile
CHANGED
@@ -1,38 +1,6 @@
|
|
1
|
-
|
2
|
-
require 'bones'
|
3
|
-
rescue LoadError
|
4
|
-
abort '### Please install the "bones" gem ###'
|
5
|
-
end
|
6
|
-
|
7
|
-
namespace :win do
|
8
|
-
|
9
|
-
desc 'Build and install gem under Windows. Mr Bones just has to break things using tar.'
|
10
|
-
task :install do
|
11
|
-
PKG_PATH = File.join(File.dirname(__FILE__), 'pkg')
|
12
|
-
NAME = File.basename(File.dirname(__FILE__))
|
13
|
-
rm_rf PKG_PATH
|
14
|
-
system "gem build #{NAME}.gemspec"
|
15
|
-
mkdir_p PKG_PATH
|
16
|
-
mv "#{NAME}-*.gem", PKG_PATH
|
17
|
-
system "gem install #{PKG_PATH}/#{NAME}-*.gem"
|
18
|
-
end
|
19
|
-
end
|
1
|
+
require 'bundler/gem_tasks'
|
20
2
|
|
21
3
|
require 'rspec/core/rake_task'
|
22
|
-
RSpec::Core::RakeTask.new
|
4
|
+
RSpec::Core::RakeTask.new
|
23
5
|
|
24
6
|
task :default => :spec
|
25
|
-
|
26
|
-
Bones {
|
27
|
-
name 'ffi-rzmq'
|
28
|
-
authors 'Chuck Remes'
|
29
|
-
email 'cremes@mac.com'
|
30
|
-
url 'http://github.com/chuckremes/ffi-rzmq'
|
31
|
-
readme_file 'README.rdoc'
|
32
|
-
ruby_opts.clear # turn off warnings
|
33
|
-
|
34
|
-
# necessary for MRI; unnecessary for JRuby and RBX
|
35
|
-
# can't enable this until JRuby & RBX have a way of dealing with it cleanly
|
36
|
-
#depend_on 'ffi', '>= 1.0.0'
|
37
|
-
}
|
38
|
-
|
data/examples/README.rdoc
CHANGED
@@ -10,7 +10,7 @@ All of the examples assume the lib directory containing the gem sources is two d
|
|
10
10
|
|
11
11
|
The ZeroMQ C libraries need to be downloaded, compiled and installed separately from the gem. Please see http://www.zeromq.org/area:download for links to the downloadable files along with some simple installation instructions. Also, be sure to check the FAQ if you run into problems with compiling.
|
12
12
|
|
13
|
-
This gem auto-configures itself to conform to the API for 0mq 2.1.x
|
13
|
+
This gem auto-configures itself to conform to the API for 0mq 2.1.x and 3.1.x. The 0mq project started making backward-incompatible changes with the 3.x branch. Rather than create separate gems, this one handles all of them.
|
14
14
|
|
15
15
|
It is possible to install the libzmq* files directly into the gem in the ext/ directory. This directory is checked for loadable libraries first before it falls back to checking the system paths.
|
16
16
|
|
@@ -0,0 +1,139 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
3
|
+
|
4
|
+
|
5
|
+
# Within a single process, we start up two threads. One thread has a REQ (request)
|
6
|
+
# socket and the second thread has a REP (reply) socket. We measure the
|
7
|
+
# *round-trip* latency between these sockets. Only *one* message is in flight at
|
8
|
+
# any given moment.
|
9
|
+
#
|
10
|
+
# This example also illustrates how a single context can be shared amongst several
|
11
|
+
# threads. Sharing a single context also allows a user to specify the "inproc"
|
12
|
+
# transport in addition to "tcp" and "ipc".
|
13
|
+
#
|
14
|
+
# % ruby latency_measurement.rb tcp://127.0.0.1:5555 1024 1_000_000
|
15
|
+
#
|
16
|
+
# % ruby latency_measurement.rb inproc://lm_sock 1024 1_000_000
|
17
|
+
#
|
18
|
+
|
19
|
+
if ARGV.length < 3
|
20
|
+
puts "usage: ruby latency_measurement.rb <connect-to> <message-size> <roundtrip-count>"
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
|
24
|
+
link = ARGV[0]
|
25
|
+
message_size = ARGV[1].to_i
|
26
|
+
roundtrip_count = ARGV[2].to_i
|
27
|
+
|
28
|
+
def assert(rc)
|
29
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
master_context = ZMQ::Context.new
|
34
|
+
rescue ContextError => e
|
35
|
+
STDERR.puts "Failed to allocate context or socket!"
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class Receiver
|
41
|
+
def initialize context, link, size, count
|
42
|
+
@context = context
|
43
|
+
@link = link
|
44
|
+
@size = size
|
45
|
+
@count = count
|
46
|
+
|
47
|
+
begin
|
48
|
+
@socket = @context.socket(ZMQ::REP)
|
49
|
+
rescue ContextError => e
|
50
|
+
STDERR.puts "Failed to allocate REP socket!"
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
|
54
|
+
assert(@socket.setsockopt(ZMQ::LINGER, 100))
|
55
|
+
|
56
|
+
assert(@socket.setsockopt(ZMQ::HWM, 100))
|
57
|
+
|
58
|
+
assert(@socket.bind(@link))
|
59
|
+
end
|
60
|
+
|
61
|
+
def run
|
62
|
+
@count.times do
|
63
|
+
string = ''
|
64
|
+
assert(@socket.recv_string(string, 0))
|
65
|
+
|
66
|
+
raise "Message size doesn't match, expected [#{@size}] but received [#{string.size}]" if @size != string.size
|
67
|
+
|
68
|
+
assert(@socket.send_string(string, 0))
|
69
|
+
end
|
70
|
+
|
71
|
+
assert(@socket.close)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Transmitter
|
76
|
+
def initialize context, link, size, count
|
77
|
+
@context = context
|
78
|
+
@link = link
|
79
|
+
@size = size
|
80
|
+
@count = count
|
81
|
+
|
82
|
+
begin
|
83
|
+
@socket = @context.socket(ZMQ::REQ)
|
84
|
+
rescue ContextError => e
|
85
|
+
STDERR.puts "Failed to allocate REP socket!"
|
86
|
+
raise
|
87
|
+
end
|
88
|
+
|
89
|
+
assert(@socket.setsockopt(ZMQ::LINGER, 100))
|
90
|
+
|
91
|
+
assert(@socket.setsockopt(ZMQ::HWM, 100))
|
92
|
+
|
93
|
+
assert(@socket.connect(@link))
|
94
|
+
end
|
95
|
+
|
96
|
+
def run
|
97
|
+
msg = "#{ '3' * @size }"
|
98
|
+
|
99
|
+
elapsed = elapsed_microseconds do
|
100
|
+
@count.times do
|
101
|
+
assert(@socket.send_string(msg, 0))
|
102
|
+
assert(@socket.recv_string(msg, 0))
|
103
|
+
|
104
|
+
raise "Message size doesn't match, expected [#{@size}] but received [#{msg.size}]" if @size != msg.size
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
latency = elapsed / @count / 2
|
109
|
+
|
110
|
+
puts "message size: %i [B]" % @size
|
111
|
+
puts "roundtrip count: %i" % @count
|
112
|
+
puts "throughput (msgs/s): %i" % (@count / (elapsed / 1_000_000))
|
113
|
+
puts "mean latency: %.3f [us]" % latency
|
114
|
+
assert(@socket.close)
|
115
|
+
end
|
116
|
+
|
117
|
+
def elapsed_microseconds(&blk)
|
118
|
+
start = Time.now
|
119
|
+
yield
|
120
|
+
value = ((Time.now - start) * 1_000_000)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
threads = []
|
125
|
+
threads << Thread.new do
|
126
|
+
receiver = Receiver.new(master_context, link, message_size, roundtrip_count)
|
127
|
+
receiver.run
|
128
|
+
end
|
129
|
+
|
130
|
+
sleep 1
|
131
|
+
|
132
|
+
threads << Thread.new do
|
133
|
+
transmitter = Transmitter.new(master_context, link, message_size, roundtrip_count)
|
134
|
+
transmitter.run
|
135
|
+
end
|
136
|
+
|
137
|
+
threads.each {|t| t.join}
|
138
|
+
|
139
|
+
master_context.terminate
|
@@ -0,0 +1,93 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
3
|
+
|
4
|
+
|
5
|
+
def assert(rc)
|
6
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
7
|
+
end
|
8
|
+
|
9
|
+
link = "tcp://127.0.0.1:5555"
|
10
|
+
|
11
|
+
|
12
|
+
begin
|
13
|
+
ctx = ZMQ::Context.new
|
14
|
+
s1 = ctx.socket(ZMQ::XREQ)
|
15
|
+
s2 = ctx.socket(ZMQ::XREP)
|
16
|
+
rescue ContextError => e
|
17
|
+
STDERR.puts "Failed to allocate context or socket"
|
18
|
+
raise
|
19
|
+
end
|
20
|
+
|
21
|
+
s1.identity = 'socket1.xreq'
|
22
|
+
s2.identity = 'socket2.xrep'
|
23
|
+
|
24
|
+
assert(s1.setsockopt(ZMQ::LINGER, 100))
|
25
|
+
assert(s2.setsockopt(ZMQ::LINGER, 100))
|
26
|
+
|
27
|
+
assert(s1.bind(link))
|
28
|
+
assert(s2.connect(link))
|
29
|
+
|
30
|
+
poller = ZMQ::Poller.new
|
31
|
+
poller.register_readable(s2)
|
32
|
+
poller.register_writable(s1)
|
33
|
+
|
34
|
+
start_time = Time.now
|
35
|
+
@unsent = true
|
36
|
+
|
37
|
+
until @done do
|
38
|
+
assert(poller.poll_nonblock)
|
39
|
+
|
40
|
+
# send the message after 5 seconds
|
41
|
+
if Time.now - start_time > 5 && @unsent
|
42
|
+
puts "sending payload nonblocking"
|
43
|
+
|
44
|
+
5.times do |i|
|
45
|
+
payload = "#{ i.to_s * 40 }"
|
46
|
+
assert(s1.send_string(payload, ZMQ::NOBLOCK))
|
47
|
+
end
|
48
|
+
@unsent = false
|
49
|
+
end
|
50
|
+
|
51
|
+
# check for messages after 1 second
|
52
|
+
if Time.now - start_time > 1
|
53
|
+
poller.readables.each do |sock|
|
54
|
+
|
55
|
+
if sock.identity =~ /xrep/
|
56
|
+
routing_info = ''
|
57
|
+
assert(sock.recv_string(routing_info, ZMQ::NOBLOCK))
|
58
|
+
puts "routing_info received [#{routing_info}] on socket.identity [#{sock.identity}]"
|
59
|
+
else
|
60
|
+
routing_info = nil
|
61
|
+
received_msg = ''
|
62
|
+
assert(sock.recv_string(received_msg, ZMQ::NOBLOCK))
|
63
|
+
|
64
|
+
# skip to the next iteration if received_msg is nil; that means we got an EAGAIN
|
65
|
+
next unless received_msg
|
66
|
+
puts "message received [#{received_msg}] on socket.identity [#{sock.identity}]"
|
67
|
+
end
|
68
|
+
|
69
|
+
while sock.more_parts? do
|
70
|
+
received_msg = ''
|
71
|
+
assert(sock.recv_string(received_msg, ZMQ::NOBLOCK))
|
72
|
+
|
73
|
+
puts "message received [#{received_msg}]"
|
74
|
+
end
|
75
|
+
|
76
|
+
puts "kick back a reply"
|
77
|
+
assert(sock.send_string(routing_info, ZMQ::SNDMORE | ZMQ::NOBLOCK)) if routing_info
|
78
|
+
time = Time.now.strftime "%Y-%m-%dT%H:%M:%S.#{Time.now.usec}"
|
79
|
+
reply = "reply " + sock.identity.upcase + " #{time}"
|
80
|
+
puts "sent reply [#{reply}], #{time}"
|
81
|
+
assert(sock.send_string(reply))
|
82
|
+
@done = true
|
83
|
+
poller.register_readable(s1)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
puts "executed in [#{Time.now - start_time}] seconds"
|
89
|
+
|
90
|
+
assert(s1.close)
|
91
|
+
assert(s2.close)
|
92
|
+
|
93
|
+
ctx.terminate
|
@@ -0,0 +1,139 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
3
|
+
|
4
|
+
|
5
|
+
# Within a single process, we start up two threads. One thread has a REQ (request)
|
6
|
+
# socket and the second thread has a REP (reply) socket. We measure the
|
7
|
+
# *round-trip* latency between these sockets. Only *one* message is in flight at
|
8
|
+
# any given moment.
|
9
|
+
#
|
10
|
+
# This example also illustrates how a single context can be shared amongst several
|
11
|
+
# threads. Sharing a single context also allows a user to specify the "inproc"
|
12
|
+
# transport in addition to "tcp" and "ipc".
|
13
|
+
#
|
14
|
+
# % ruby latency_measurement.rb tcp://127.0.0.1:5555 1024 1_000_000
|
15
|
+
#
|
16
|
+
# % ruby latency_measurement.rb inproc://lm_sock 1024 1_000_000
|
17
|
+
#
|
18
|
+
|
19
|
+
if ARGV.length < 3
|
20
|
+
puts "usage: ruby latency_measurement.rb <connect-to> <message-size> <roundtrip-count>"
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
|
24
|
+
link = ARGV[0]
|
25
|
+
message_size = ARGV[1].to_i
|
26
|
+
roundtrip_count = ARGV[2].to_i
|
27
|
+
|
28
|
+
def assert(rc)
|
29
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
master_context = ZMQ::Context.new
|
34
|
+
rescue ContextError => e
|
35
|
+
STDERR.puts "Failed to allocate context or socket!"
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class Receiver
|
41
|
+
def initialize context, link, size, count
|
42
|
+
@context = context
|
43
|
+
@link = link
|
44
|
+
@size = size
|
45
|
+
@count = count
|
46
|
+
|
47
|
+
begin
|
48
|
+
@socket = @context.socket(ZMQ::REP)
|
49
|
+
rescue ContextError => e
|
50
|
+
STDERR.puts "Failed to allocate REP socket!"
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
|
54
|
+
assert(@socket.setsockopt(ZMQ::LINGER, 100))
|
55
|
+
assert(@socket.setsockopt(ZMQ::RCVHWM, 100))
|
56
|
+
assert(@socket.setsockopt(ZMQ::SNDHWM, 100))
|
57
|
+
|
58
|
+
assert(@socket.bind(@link))
|
59
|
+
end
|
60
|
+
|
61
|
+
def run
|
62
|
+
@count.times do
|
63
|
+
string = ''
|
64
|
+
assert(@socket.recv_string(string, 0))
|
65
|
+
|
66
|
+
raise "Message size doesn't match, expected [#{@size}] but received [#{string.size}]" if @size != string.size
|
67
|
+
|
68
|
+
assert(@socket.send_string(string, 0))
|
69
|
+
end
|
70
|
+
|
71
|
+
assert(@socket.close)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Transmitter
|
76
|
+
def initialize context, link, size, count
|
77
|
+
@context = context
|
78
|
+
@link = link
|
79
|
+
@size = size
|
80
|
+
@count = count
|
81
|
+
|
82
|
+
begin
|
83
|
+
@socket = @context.socket(ZMQ::REQ)
|
84
|
+
rescue ContextError => e
|
85
|
+
STDERR.puts "Failed to allocate REP socket!"
|
86
|
+
raise
|
87
|
+
end
|
88
|
+
|
89
|
+
assert(@socket.setsockopt(ZMQ::LINGER, 100))
|
90
|
+
assert(@socket.setsockopt(ZMQ::RCVHWM, 100))
|
91
|
+
assert(@socket.setsockopt(ZMQ::SNDHWM, 100))
|
92
|
+
|
93
|
+
assert(@socket.connect(@link))
|
94
|
+
end
|
95
|
+
|
96
|
+
def run
|
97
|
+
msg = "#{ '3' * @size }"
|
98
|
+
|
99
|
+
elapsed = elapsed_microseconds do
|
100
|
+
@count.times do
|
101
|
+
assert(@socket.send_string(msg, 0))
|
102
|
+
assert(@socket.recv_string(msg, 0))
|
103
|
+
|
104
|
+
raise "Message size doesn't match, expected [#{@size}] but received [#{msg.size}]" if @size != msg.size
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
latency = elapsed / @count / 2
|
109
|
+
|
110
|
+
puts "message size: %i [B]" % @size
|
111
|
+
puts "roundtrip count: %i" % @count
|
112
|
+
puts "throughput (msgs/s): %i" % (@count / (elapsed / 1_000_000))
|
113
|
+
puts "mean latency: %.3f [us]" % latency
|
114
|
+
assert(@socket.close)
|
115
|
+
end
|
116
|
+
|
117
|
+
def elapsed_microseconds(&blk)
|
118
|
+
start = Time.now
|
119
|
+
yield
|
120
|
+
value = ((Time.now - start) * 1_000_000)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
threads = []
|
125
|
+
threads << Thread.new do
|
126
|
+
receiver = Receiver.new(master_context, link, message_size, roundtrip_count)
|
127
|
+
receiver.run
|
128
|
+
end
|
129
|
+
|
130
|
+
sleep 1
|
131
|
+
|
132
|
+
threads << Thread.new do
|
133
|
+
transmitter = Transmitter.new(master_context, link, message_size, roundtrip_count)
|
134
|
+
transmitter.run
|
135
|
+
end
|
136
|
+
|
137
|
+
threads.each {|t| t.join}
|
138
|
+
|
139
|
+
master_context.terminate
|