On Wednesday 19, Christian Martinez wrote:
> Maybe I'm missing something here, but are people asserting here that one
> can't do a request reply MEP with various RPC technologies and exceed 1000
> 1KB messages a second?

With only 1 client and 1 concurrent request, then yes.  The msg/sec throughput 
will be limited by latency in this case.  Both client & server will be maxing 
out their CPUs, each will be sitting idle most of the time waiting for a reply 
from the other side.  If you want more throughput, then you need to increase 
the number of concurrent requests sent by the clients.

I did a quick comparison of zmq & (nginx + ab).

Hardware:
Client: Laptop 100Mbit connection 1 core 2.2Ghz AMD
Server: desktop 1Gbit connection 8 core 3.6Ghz AMD

Test parameters:
requests: 400,000
max concurrent: 8 concurrent requests, 8 concurrent TCP sockets
request/response size: 1k

zmq: (using lua-zmq + LuaJIT, see attached Lua scripts)
Client: 8 threads, so 8 concurrent requests, cpu maxed out.
Server: 1 thread, low cpu usage
throughput: 10,990 reqs/sec, about 90Mbits (both directions)
bottleneck: client cpu maxed & client bandwidth.

nginx: (1k post data including HTTP headers, 1k response data including HTTP 
headers)
Client: ab (apache bench) keep-alive, 8 concurrent requests, cpu maxed out.
Server: nginx keepalive_requests = 400,000, limited to 1 worker process.
throughput: 9,055 reqs/sec, about 74Mbits (both directions)
bottleneck: client cpu maxed.

For reference using PUB/SUB the server can push 1k size messages out at 11,335 
msg/sec, or about 92Mbits (in one direction server -> client).  The bottleneck 
here is the 100Mbit client connection.

-- 
Robert G. Jakabosky
-- Copyright (c) 2010 Aleksey Yeschenko <[email protected]>
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.

if not arg[3] then
    print("usage: lua local_lat.lua <bind-to> <message-size> <roundtrip-count>")
    os.exit()
end

local bind_to = arg[1]
local message_size = tonumber(arg[2])
local roundtrip_count = tonumber(arg[3])

local zmq = require"zmq"

local ctx = zmq.init(1)
local s = ctx:socket(zmq.REP)
s:bind(bind_to)

local msg = zmq.zmq_msg_t()

local timer

for i = 1, roundtrip_count do
	assert(s:recv_msg(msg))
	if not timer then
		timer = zmq.stopwatch_start()
	end
	assert(msg:size() == message_size, "Invalid message size")
	assert(s:send_msg(msg))
end

local elapsed = timer:stop()

s:close()
ctx:term()

local latency = elapsed / roundtrip_count / 2

print(string.format("mean latency: %.3f [us]", latency))
local secs = elapsed / (1000 * 1000)
print(string.format("elapsed = %f", secs))
print(string.format("msg/sec = %f", roundtrip_count / secs))

-- Copyright (c) 2011 Robert G. Jakabosky <[email protected]>
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.

if #arg < 1 then
    print("usage: lua " .. arg[0] .. " [message-size] [roundtrip-count] [concurrent] [connect-to]")
end

local message_size = tonumber(arg[1] or 1)
local roundtrip_count = tonumber(arg[2] or 100000)
local concurrent = tonumber(arg[3]) or 1
local connect_to = arg[4] or 'tcp://localhost:5555'

local zmq = require"zmq"
local zthreads = require"zmq.threads"

local child_code = [[
	local connect_to, message_size, roundtrip_count = ...

	local zmq = require"zmq"
	local zthreads = require"zmq.threads"

	local ctx = zthreads.get_parent_ctx()
	local s = ctx:socket(zmq.REQ)
	s:connect(connect_to)

	local data = ("0"):rep(message_size)
	local msg = zmq.zmq_msg_t.init_size(message_size)

	local timer = zmq.stopwatch_start()

	for i = 1, roundtrip_count do
		assert(s:send_msg(msg))
		assert(s:recv_msg(msg))
		assert(msg:size() == message_size, "Invalid message size")
	end

	local elapsed = timer:stop()

	s:close()

	local latency = elapsed / roundtrip_count / 2

	print(string.format("message size: %i [B]", message_size))
	print(string.format("roundtrip count: %i", roundtrip_count))
	print(string.format("mean latency: %.3f [us]", latency))

	local secs = elapsed / (1000 * 1000)
	print(string.format("elapsed = %f", secs))
	print(string.format("msg/sec = %f", roundtrip_count / secs))

]]

local ctx = zmq.init(1)

local child_threads = {}

local per_count = roundtrip_count / concurrent
for i=1,concurrent do
	local child = zthreads.runstring(ctx, child_code, connect_to, message_size, per_count)
	child:start()
	child_threads[i] = child
end

for i=1,concurrent do
	child_threads[i]:join()
end

ctx:term()

_______________________________________________
zeromq-dev mailing list
[email protected]
http://lists.zeromq.org/mailman/listinfo/zeromq-dev

Reply via email to