ffi-rzmq 0.8.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS.txt +1 -0
- data/History.txt +35 -0
- data/README.rdoc +48 -15
- data/Rakefile +7 -2
- data/examples/README.rdoc +21 -76
- data/examples/{local_lat.rb → v2api/local_lat.rb} +27 -12
- data/examples/v2api/local_lat_poll.rb +66 -0
- data/examples/{local_throughput.rb → v2api/local_throughput.rb} +24 -9
- data/examples/v2api/publish_subscribe.rb +82 -0
- data/examples/{remote_lat.rb → v2api/remote_lat.rb} +26 -8
- data/examples/v2api/remote_throughput.rb +39 -0
- data/examples/v2api/reqrep_poll.rb +62 -0
- data/examples/v2api/request_response.rb +40 -0
- data/examples/v2api/throughput_measurement.rb +138 -0
- data/examples/v3api/local_lat.rb +59 -0
- data/examples/v3api/local_lat_poll.rb +66 -0
- data/examples/v3api/local_throughput.rb +65 -0
- data/examples/v3api/publish_subscribe.rb +82 -0
- data/examples/v3api/remote_lat.rb +71 -0
- data/examples/v3api/remote_throughput.rb +47 -0
- data/examples/v3api/reqrep_poll.rb +62 -0
- data/examples/v3api/request_response.rb +40 -0
- data/examples/v3api/throughput_measurement.rb +166 -0
- data/ext/README +5 -0
- data/ffi-rzmq.gemspec +4 -4
- data/lib/ffi-rzmq.rb +4 -1
- data/lib/ffi-rzmq/constants.rb +178 -0
- data/lib/ffi-rzmq/context.rb +61 -45
- data/lib/ffi-rzmq/device.rb +22 -9
- data/lib/ffi-rzmq/exceptions.rb +0 -98
- data/lib/ffi-rzmq/libc.rb +19 -0
- data/lib/ffi-rzmq/libzmq.rb +188 -0
- data/lib/ffi-rzmq/message.rb +33 -40
- data/lib/ffi-rzmq/poll.rb +49 -52
- data/lib/ffi-rzmq/socket.rb +902 -392
- data/lib/ffi-rzmq/util.rb +101 -0
- data/spec/context_spec.rb +47 -21
- data/spec/device_spec.rb +78 -58
- data/spec/message_spec.rb +90 -12
- data/spec/multipart_spec.rb +162 -0
- data/spec/nonblocking_recv_spec.rb +325 -0
- data/spec/pushpull_spec.rb +95 -34
- data/spec/reqrep_spec.rb +55 -20
- data/spec/socket_spec.rb +353 -204
- data/spec/spec_helper.rb +46 -3
- data/version.txt +1 -1
- metadata +91 -66
- data/examples/local_lat_poll.rb +0 -54
- data/examples/local_lat_zerocopy.rb +0 -24
- data/examples/publish_subscribe.rb +0 -52
- data/examples/remote_lat_zerocopy.rb +0 -35
- data/examples/remote_throughput.rb +0 -27
- data/examples/reqrep_poll.rb +0 -49
- data/examples/request_response.rb +0 -23
- data/lib/ffi-rzmq/wrapper.rb +0 -121
- data/lib/ffi-rzmq/zmq.rb +0 -198
data/AUTHORS.txt
CHANGED
data/History.txt
CHANGED
@@ -1,3 +1,38 @@
|
|
1
|
+
== 0.9.0 / 20110930
|
2
|
+
* Changed the behavior of every method that used to produce exceptions.
|
3
|
+
The methods now behave more like the C API functions. They return
|
4
|
+
result codes instead of raising exceptions. Further, the "receive"
|
5
|
+
methods on Socket now all take an empty string as a buffer to read
|
6
|
+
the message into.
|
7
|
+
This is a BREAKING CHANGE and is NOT backward compatible with earlier
|
8
|
+
releases. I apologize for the inconvenience, but this API will be
|
9
|
+
much easier to test/spec and maintain. It will also allow for the
|
10
|
+
production of more logical code.
|
11
|
+
|
12
|
+
* Major refactoring of Socket internals so that a single gem can
|
13
|
+
support libzmq 2.x, 3.x and 4.x APIs without any user intervention.
|
14
|
+
The correct libzmq version is detected at runtime and used to
|
15
|
+
configure the Ruby classes to conform to the proper API.
|
16
|
+
|
17
|
+
* Added Socket#recvmsgs as a convenience method for receiving a
|
18
|
+
multipart message into an array of Messages.
|
19
|
+
|
20
|
+
* Added support for new 0mq API introduced in the 3.0 branch.
|
21
|
+
API mostly changed for sending and receiving messages with new
|
22
|
+
POSIX-compliant send() and recv() functions. The original
|
23
|
+
functions were renamed sendmsg() and recvmsg().
|
24
|
+
Additionally, most getsockopt() and setsockopt() calls now use
|
25
|
+
an int (4 bytes) instead of a mish-mash of 32-bit and 64-bit
|
26
|
+
values.
|
27
|
+
For a full list of differences, visit the 0mq wiki page at:
|
28
|
+
http://www.zeromq.org/docs:3-0-upgrade
|
29
|
+
|
30
|
+
* Created a new ext/ directory so that users can copy the libzmq*
|
31
|
+
library files directly into the gem for easier distribution. This
|
32
|
+
path is checked *before* the usual system paths.
|
33
|
+
|
34
|
+
* Rewrote all examples to use the revised API.
|
35
|
+
|
1
36
|
== 0.8.2 / 20110728
|
2
37
|
* Fixed major bug with Socket#setsockopt when writing 8-byte longs.
|
3
38
|
|
data/README.rdoc
CHANGED
@@ -9,12 +9,44 @@ 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.
|
14
|
+
The gem auto-configures itself to expose the API conforming to the loaded
|
15
|
+
C library.
|
16
|
+
|
12
17
|
The impetus behind this library was to provide support for ZeroMQ in
|
13
|
-
JRuby which has native threads. Unlike MRI,
|
14
|
-
Rubinius
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
JRuby which has native threads. Unlike MRI, which has a GIL, JRuby and
|
19
|
+
Rubinius allow for threaded access to Ruby code from outside extensions.
|
20
|
+
ZeroMQ is heavily threaded, so until the MRI runtime removes its GIL,
|
21
|
+
JRuby and Rubinius will likely be the best environments to run this library.
|
22
|
+
|
23
|
+
== Breaking API Changes from 0.8.x to 0.9.0
|
24
|
+
|
25
|
+
There has been a major change in API from the 0.8.x series to 0.9.0.
|
26
|
+
Previously the Socket#send and Socket#recv methods could raise exceptions
|
27
|
+
when there was an error. They also returned true or false depending on
|
28
|
+
the success of failure of the operation. Mixing and matching return codes
|
29
|
+
and exceptions is a terrible idea in practice, so all of the exception
|
30
|
+
code from Socket has been removed.
|
31
|
+
|
32
|
+
The Poller and Message class have also had their exceptions removed for
|
33
|
+
returning errors on instance methods.
|
34
|
+
|
35
|
+
Unfortunately, it isn't possible to indicate that a call to #new has failed
|
36
|
+
by returning nil. A call to #new that fails needs to raise an exception to
|
37
|
+
indicate failure. (This isn't strictly true; this behavior can be changed
|
38
|
+
but it is not a standard Ruby idiom and is therefore a surprising, and
|
39
|
+
poor, thing to do.) Classes that formerly raised exceptions during allocation
|
40
|
+
now offer a factory method #create that will either return a successfully
|
41
|
+
constructed object or nil. Code should check for nil returns before using
|
42
|
+
the object.
|
43
|
+
|
44
|
+
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. If you
|
46
|
+
prefer the original API, it should be relatively simple to wrap these
|
47
|
+
classes up to provide the original API.
|
48
|
+
|
49
|
+
All example code has been updated to use the new Ruby API.
|
18
50
|
|
19
51
|
== PERFORMANCE
|
20
52
|
|
@@ -24,19 +56,22 @@ Check out the latest performance results:
|
|
24
56
|
|
25
57
|
== FEATURES/PROBLEMS:
|
26
58
|
|
27
|
-
This gem needs more tests.
|
28
|
-
|
29
|
-
|
59
|
+
This gem needs more tests. This gem has been battle tested by myself
|
60
|
+
and others for over a year, so I am fairly confident that it is solid.
|
61
|
+
However, it is inevitable that there will be bugs, so please open
|
62
|
+
issues for them here or fork this project, fix them, and send me a pull
|
63
|
+
request.
|
30
64
|
|
31
65
|
The 'ffi' gem has dropped support for MRI 1.8.x. Since this project relies
|
32
66
|
on that gem to load and run this code, then this project also no longer
|
33
|
-
supports MRI 1.8.x.
|
67
|
+
supports MRI 1.8.x. I recommend JRuby for the best performance and
|
68
|
+
stability.
|
34
69
|
|
35
70
|
All features are implemented.
|
36
71
|
|
37
72
|
== SYNOPSIS:
|
38
73
|
|
39
|
-
|
74
|
+
0mq API v2 client code:
|
40
75
|
|
41
76
|
require 'rubygems'
|
42
77
|
require 'ffi-rzmq'
|
@@ -61,10 +96,8 @@ Client code:
|
|
61
96
|
s.send_string msg, 0
|
62
97
|
end
|
63
98
|
|
64
|
-
# give the lib time to flush any remaining messages
|
65
|
-
sleep 1
|
66
99
|
|
67
|
-
|
100
|
+
0mq API v2 server code:
|
68
101
|
|
69
102
|
require 'rubygems'
|
70
103
|
require 'ffi-rzmq'
|
@@ -102,7 +135,7 @@ I highly recommend visiting the Learn Ruby 0mq project for a bunch of good code
|
|
102
135
|
|
103
136
|
== REQUIREMENTS:
|
104
137
|
|
105
|
-
* 0mq 2.
|
138
|
+
* 0mq 2.1.x or later; 2.0.x is no longer supported
|
106
139
|
|
107
140
|
The ZeroMQ library must be installed on your system in a well-known location
|
108
141
|
like /usr/local/lib. This is the default for new ZeroMQ installs.
|
@@ -140,7 +173,7 @@ To build from git master:
|
|
140
173
|
|
141
174
|
(The MIT License)
|
142
175
|
|
143
|
-
Copyright (c)
|
176
|
+
Copyright (c) 2011 Chuck Remes
|
144
177
|
|
145
178
|
Permission is hereby granted, free of charge, to any person obtaining
|
146
179
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -13,11 +13,16 @@ namespace :win do
|
|
13
13
|
rm_rf PKG_PATH
|
14
14
|
system "gem build #{NAME}.gemspec"
|
15
15
|
mkdir_p PKG_PATH
|
16
|
-
mv "#{NAME}
|
17
|
-
system "gem install #{PKG_PATH}/#{NAME}
|
16
|
+
mv "#{NAME}-*.gem", PKG_PATH
|
17
|
+
system "gem install #{PKG_PATH}/#{NAME}-*.gem"
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
require 'rspec/core/rake_task'
|
22
|
+
RSpec::Core::RakeTask.new(:spec)
|
23
|
+
|
24
|
+
task :default => :spec
|
25
|
+
|
21
26
|
Bones {
|
22
27
|
name 'ffi-rzmq'
|
23
28
|
authors 'Chuck Remes'
|
data/examples/README.rdoc
CHANGED
@@ -2,17 +2,21 @@
|
|
2
2
|
|
3
3
|
== Requirements
|
4
4
|
|
5
|
-
1.
|
5
|
+
1. lib dir
|
6
6
|
|
7
|
-
All of the examples assume the gem
|
7
|
+
All of the examples assume the lib directory containing the gem sources is two directories up from the location of the example.
|
8
8
|
|
9
9
|
2. Installed libzmq library
|
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
|
-
3.
|
13
|
+
This gem auto-configures itself to conform to the API for 0mq 2.1.x, 3.x and 4.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
|
+
|
17
|
+
3. One terminal window
|
18
|
+
|
19
|
+
ZeroMQ is used to build network applications. At minimum, there is a "client" application and a "server" application that talk to each other over the network, IPC or an internal thread queue. Several of the examples start the client and server components within separate threads or use the polling mechanism to interleave I/O operations amongst several sockets. A few examples need two terminal windows because the client and server code is in separate files (local_lat.rb/remote_lat.rb, local_throughput.rb/remote_throughput.rb).
|
16
20
|
|
17
21
|
== Latency Test
|
18
22
|
|
@@ -20,34 +24,26 @@ The examples include a latency performance test. The example sets up a pair of R
|
|
20
24
|
|
21
25
|
==== Files
|
22
26
|
|
23
|
-
*
|
24
|
-
* remote_lat.rb
|
27
|
+
* latency_measurement.rb
|
25
28
|
|
26
29
|
==== Arguments
|
27
30
|
|
28
31
|
The remote_lat.rb program takes 3 arguments:
|
29
32
|
|
30
|
-
[
|
33
|
+
[link_address] Requires a transport string of the format "transport"://"endpoint"<:><port>. For example, tcp://127.0.0.1:5555
|
31
34
|
|
32
35
|
[message size] Size of each message measured in bytes. Allowable range is 1 to 2^(64-1).
|
33
36
|
|
34
37
|
[message count] The number of round-trips used for the latency measurements. Allowable range is 1 to 2^(64-1).
|
35
38
|
|
36
|
-
|
37
|
-
The local_lat.rb program also takes 3 arguments. They should exactly mirror the arguments given to remote_lat.rb.
|
38
|
-
|
39
39
|
|
40
40
|
==== Execution
|
41
41
|
|
42
|
-
In
|
43
|
-
|
44
|
-
% ruby remote_lat.rb tcp://127.0.0.1:5555 1024 100_000
|
42
|
+
In an open terminal window, execute the latency_measurement.rb file.
|
45
43
|
|
46
|
-
|
44
|
+
% ruby latency_measurement.rb tcp://127.0.0.1:5555 1024 100_000
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
On a relatively new system, it can run 100k messages in under 30 seconds. When complete, the remote_lat.rb program prints out a few statistics and exits. The local_lat.rb program does not print any message when it exits.
|
46
|
+
On a relatively new system, it can run 100k messages in under 30 seconds. When complete, the program prints out a few statistics and exits.
|
51
47
|
|
52
48
|
Running with a larger "message count" will yield a more accurate latency measurement since nearly all Ruby runtimes require a little warm up time to hit their stride. I recommend 100k as a minimum while 10 million is better for determining a true measure.
|
53
49
|
|
@@ -55,87 +51,36 @@ On a desktop computer purchased in 2007, all of the Ruby runtimes report a laten
|
|
55
51
|
|
56
52
|
|
57
53
|
|
58
|
-
== Zero Copy Latency Test
|
59
|
-
|
60
|
-
The examples include a zero copy latency performance test. The example sets up a pair of REQ/REP sockets and send a message back and forth as fast as possible. There is only a single message in flight at any given moment. The time required to send the message the requested number of times determines overall single-message latency for this type of socket.
|
61
|
-
|
62
|
-
The difference between this test and the first latency test has to do with the management of the sent and received messages. This test does not examine or copy the contents of the message at all. Instead, its mere presence is sufficient to echo it back to the other program. Therefore, this test is doing a lot less work for each message send & receive than the first latency test. Also, this test more perfectly mirrors the work being performed by the C latency test.
|
63
|
-
|
64
|
-
==== Files
|
65
|
-
|
66
|
-
* local_lat_zerocopy.rb
|
67
|
-
* remote_lat_zerocopy.rb
|
68
|
-
|
69
|
-
==== Arguments
|
70
|
-
|
71
|
-
The remote_lat_zerocopy.rb program takes 3 arguments:
|
72
|
-
|
73
|
-
[bind_to] Requires a transport string of the format "transport"://"endpoint"<:><port>. For example, tcp://127.0.0.1:5555
|
74
|
-
|
75
|
-
[message size] Size of each message measured in bytes. Allowable range is 1 to 2^(64-1).
|
76
|
-
|
77
|
-
[message count] The number of round-trips used for the latency measurements. Allowable range is 1 to 2^(64-1).
|
78
|
-
|
79
|
-
|
80
|
-
The local_lat_zerocopy.rb program also takes 3 arguments. They should exactly mirror the arguments given to remote_lat_zerocopy.rb.
|
81
|
-
|
82
|
-
|
83
|
-
==== Execution
|
84
|
-
|
85
|
-
In one of the terminals, start up the remote_lat_zerocopy.rb program first. It *must* be launched first so that it can be ready and waiting for the first message sent by the local_lat_zerocopy.rb program.
|
86
|
-
|
87
|
-
% ruby remote_lat_zerocopy.rb tcp://127.0.0.1:5555 1024 100_000
|
88
|
-
|
89
|
-
In the second terminal window, start up the local_lat.rb program too.
|
90
|
-
|
91
|
-
% ruby local_lat_zerocopy.rb tcp://127.0.0.1:5555 1024 100_000
|
92
|
-
|
93
|
-
On a relatively new system, it can run 100k messages in under 30 seconds. When complete, the remote_lat_zerocopy.rb program prints out a few statistics and exits. The local_lat_zerocopy.rb program does not print any message when it exits.
|
94
|
-
|
95
|
-
Running with a larger "message count" will yield a more accurate latency measurement since nearly all Ruby runtimes require a little warm up time to hit their stride. I recommend 100k as a minimum while 10 million is better for determining a true measure.
|
96
|
-
|
97
|
-
On a desktop computer purchased in 2007, all of the Ruby runtimes report a latency of approximately 95 microseconds per message. For comparison, the pure C latency test reports approximately 88 microseconds of latency.
|
98
|
-
|
99
|
-
|
100
|
-
|
101
54
|
== Throughput Test
|
102
55
|
|
103
56
|
The examples include a throughput performance test. The example sets up a pair of PUB/SUB sockets and publish messages as fast as possible to a subscriber listening for every message. The publisher can send much faster than the subscriber can retrieve messages.
|
104
57
|
|
105
|
-
Since the publisher completes first,
|
58
|
+
Since the publisher completes first, the program waits for all subscribers to exit before closing the PUB socket. This is necessary because all enqueued messages are discarded when the socket is closed.
|
106
59
|
|
107
60
|
The subscriber prints some statistics when it exits.
|
108
61
|
|
109
62
|
==== Files
|
110
63
|
|
111
|
-
*
|
112
|
-
* remote_throughput.rb
|
64
|
+
* throughput_measurement.rb
|
113
65
|
|
114
66
|
==== Arguments
|
115
67
|
|
116
|
-
The
|
68
|
+
The throughput_measurement.rb program takes 3 arguments:
|
117
69
|
|
118
|
-
[
|
70
|
+
[link_address] Requires a transport string of the format "transport"://"endpoint"<:><port>. For example, tcp://127.0.0.1:5555
|
119
71
|
|
120
72
|
[message size] Size of each message measured in bytes. Allowable range is 1 to 2^(64-1).
|
121
73
|
|
122
74
|
[message count] The number of round-trips used for the latency measurements. Allowable range is 1 to 2^(64-1).
|
123
75
|
|
124
76
|
|
125
|
-
The remote_throughput.rb program also takes 3 arguments. They should exactly mirror the arguments given to local_throughput.rb.
|
126
|
-
|
127
|
-
|
128
77
|
==== Execution
|
129
78
|
|
130
|
-
In
|
79
|
+
In an open terminal, execute the throughput_measurement.rb script.
|
131
80
|
|
132
|
-
% ruby
|
81
|
+
% ruby throughput_measurement.rb tcp://127.0.0.1:5555 1024 100_000
|
133
82
|
|
134
|
-
|
135
|
-
|
136
|
-
% ruby remote_throughput.rb tcp://127.0.0.1:5555 1024 100_000
|
137
|
-
|
138
|
-
On a relatively new system, it can run 100k messages in under 10 seconds. When complete, the local_throughput.rb program prints out a few statistics and exits. The remote_throughput.rb program does not print any message when it exits.
|
83
|
+
On a relatively new system, it can run 100k messages in under 10 seconds. When complete, the program prints out a few statistics and exits.
|
139
84
|
|
140
85
|
Running with a larger "message count" will yield a more accurate latency measurement since nearly all Ruby runtimes require a little warm up time to hit their stride. I recommend 100k as a minimum while 1 million is better for determining a true measure. NOTE! The publisher can send much faster than the subscriber so the publisher's queue will grow very rapidly in RAM. For 1 million messages (or more) this can consume hundreds of megabytes or gigabytes of RAM. On my system, sending 10 million messages requires 10 GB of RAM before the subscriber can catch up.
|
141
86
|
|
@@ -145,7 +90,7 @@ On a desktop computer purchased in 2007, all of the Ruby runtimes report a throu
|
|
145
90
|
|
146
91
|
== Poll
|
147
92
|
|
148
|
-
For a reasonable example of using zmq_poll(), take a look at the reqrep_poll.rb program. It illustrates the use of zmq_poll(), as wrapped by the Ruby library, for detecting and responding to read and write events recorded on sockets. It also shows how to use ZMQ::NO_BLOCK for non-blocking send and receive.
|
93
|
+
For a reasonable example of using zmq_poll(), take a look at the reqrep_poll.rb program. It illustrates the use of zmq_poll(), as wrapped by the Ruby library, for detecting and responding to read and write events recorded on sockets. It also shows how to use ZMQ::NO_BLOCK/ZMQ::DONTWAIT for non-blocking send and receive.
|
149
94
|
|
150
95
|
==== Files
|
151
96
|
|
@@ -16,11 +16,10 @@
|
|
16
16
|
# You should have received a copy of the Lesser GNU General Public License
|
17
17
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
18
|
|
19
|
-
require '
|
20
|
-
require 'ffi-rzmq'
|
19
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
21
20
|
|
22
21
|
if ARGV.length < 3
|
23
|
-
puts "usage: local_lat <connect-to> <message-size> <roundtrip-count>"
|
22
|
+
puts "usage: ruby local_lat.rb <connect-to> <message-size> <roundtrip-count>"
|
24
23
|
exit
|
25
24
|
end
|
26
25
|
|
@@ -28,16 +27,32 @@ bind_to = ARGV[0]
|
|
28
27
|
message_size = ARGV[1].to_i
|
29
28
|
roundtrip_count = ARGV[2].to_i
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
def assert(rc)
|
31
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
ctx = ZMQ::Context.new
|
36
|
+
s = ctx.socket(ZMQ::REP)
|
37
|
+
rescue ContextError => e
|
38
|
+
STDERR.puts "Failed to allocate context or socket!"
|
39
|
+
raise
|
40
|
+
end
|
41
|
+
|
42
|
+
assert(s.setsockopt(ZMQ::LINGER, 100))
|
43
|
+
assert(s.setsockopt(ZMQ::HWM, 100))
|
44
|
+
|
45
|
+
assert(s.bind(bind_to))
|
35
46
|
|
36
47
|
roundtrip_count.times do
|
37
|
-
|
38
|
-
|
39
|
-
|
48
|
+
string = ''
|
49
|
+
assert(s.recv_string(string, 0))
|
50
|
+
|
51
|
+
raise "Message size doesn't match, expected [#{message_size}] but received [#{string.size}]" if message_size != string.size
|
52
|
+
|
53
|
+
assert(s.send_string(string, 0))
|
40
54
|
end
|
41
55
|
|
42
|
-
|
43
|
-
|
56
|
+
assert(s.close)
|
57
|
+
|
58
|
+
ctx.terminate
|
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
3
|
+
|
4
|
+
if ARGV.length < 3
|
5
|
+
puts "usage: ruby local_lat.rb <connect-to> <message-size> <roundtrip-count>"
|
6
|
+
exit
|
7
|
+
end
|
8
|
+
|
9
|
+
link = ARGV[0]
|
10
|
+
message_size = ARGV[1].to_i
|
11
|
+
roundtrip_count = ARGV[2].to_i
|
12
|
+
|
13
|
+
def assert(rc)
|
14
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
ctx = ZMQ::Context.new
|
19
|
+
s1 = ctx.socket(ZMQ::REQ)
|
20
|
+
s2 = ctx.socket(ZMQ::REP)
|
21
|
+
rescue ContextError => e
|
22
|
+
STDERR.puts "Failed to allocate context or socket!"
|
23
|
+
raise
|
24
|
+
end
|
25
|
+
|
26
|
+
assert(s1.setsockopt(ZMQ::LINGER, 100))
|
27
|
+
assert(s2.setsockopt(ZMQ::LINGER, 100))
|
28
|
+
|
29
|
+
assert(s1.connect(link))
|
30
|
+
assert(s2.bind(link))
|
31
|
+
|
32
|
+
poller = ZMQ::Poller.new
|
33
|
+
poller.register_readable(s2)
|
34
|
+
poller.register_readable(s1)
|
35
|
+
|
36
|
+
|
37
|
+
start_time = Time.now
|
38
|
+
|
39
|
+
# kick it off
|
40
|
+
message = ZMQ::Message.new("a" * message_size)
|
41
|
+
assert(s1.send(message, ZMQ::NOBLOCK))
|
42
|
+
|
43
|
+
i = roundtrip_count
|
44
|
+
|
45
|
+
until i.zero?
|
46
|
+
i -= 1
|
47
|
+
|
48
|
+
assert(poller.poll_nonblock)
|
49
|
+
|
50
|
+
poller.readables.each do |socket|
|
51
|
+
received_message = ''
|
52
|
+
assert(socket.recv_string(received_message, ZMQ::NOBLOCK))
|
53
|
+
assert(socket.send(ZMQ::Message.new(received_message), ZMQ::NOBLOCK))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
elapsed_usecs = (Time.now.to_f - start_time.to_f) * 1_000_000
|
58
|
+
latency = elapsed_usecs / roundtrip_count / 2
|
59
|
+
|
60
|
+
puts "mean latency: %.3f [us]" % latency
|
61
|
+
puts "received all messages in %.3f seconds" % (elapsed_usecs / 1_000_000)
|
62
|
+
|
63
|
+
assert(s1.close)
|
64
|
+
assert(s2.close)
|
65
|
+
|
66
|
+
ctx.terminate
|