Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ruby-mqtt 2x slower than paho-mqtt #115

Open
rgaufman opened this issue May 10, 2018 · 2 comments
Open

ruby-mqtt 2x slower than paho-mqtt #115

rgaufman opened this issue May 10, 2018 · 2 comments

Comments

@rgaufman
Copy link

rgaufman commented May 10, 2018

Hi there,

I'm comparing different ruby mqtt clients, I have this code:

client = MQTT::Client.new(username: 'testuser', password: 'testpasswd', client_id: "client_#{rand(1..1_000_000)}", host: '127.0.0.1', )
client.connect()

count = 0
loop do
  count += 1
  i = rand(1..10)

  topic = "to/timebox#{i}/cameras"
  msg = "message #{count} #{Time.now} for timebox#{i}"
  client.publish(topic, msg, true, 1)
end

Here is a version using paho-mqtt

$client = PahoMqtt::Client.new(username: 'testuser', password: 'testpasswd', client_id: "client_#{rand(1..1_000_000)}")
$client.connect('127.0.0.1', 1883)

$count = 0
def send_message
  $count += 1
  i = rand(1..$total)

  topic = "to/timebox#{i}/cameras"
  msg = "message #{$count} #{Time.now} for timebox#{i}"
  $client.publish(topic, msg, true, 1)
end

$client.on_puback { send_message } # QOS1
send_message
loop { sleep 1 } # to keep process running

I used pv to calculate how many messages are being received per second and I'm getting:

  • ruby-mqtt: 92 / sec
  • paho-mqtt: 180 /sec

I prefer the syntax of ruby-mqtt and it appears to be better maintained, but any ideas why ruby-mqtt is nearly 2x slower?

@rgaufman rgaufman changed the title Why is ruby-mqtt much slower than paho-mqtt? ruby-mqtt 2x slower than paho-mqtt May 10, 2018
@njh
Copy link
Owner

njh commented May 11, 2018

No sorry, I am not certain why there is such a different in publish speed - it must have something to do with ruby-mqtt being synchronous/using threads. paho-mqtt borrowed the packet parsing code from ruby-mqtt, so it is unlikely to be that.

tenderlove added a commit to tenderlove/ruby-mqtt that referenced this issue Jan 27, 2020
This commit changes the `publish` method to use a queue to wait for
Puback packets rather than polling a hash.  Every time the read loop
gets data or a timeout from `IO.select`, it will send a message to
everyone waiting for a `Puback` packet.  If the we're within the
deadline, then the loop executes again, if we got a packet, we'll return
the packet, and if we're outside the deadline, a `-1` is returned.

This upside is that this patch speeds up the publish method by over
100x.  Here is the benchmark:

```ruby
require "securerandom"
require "mqtt"
require "benchmark/ips"
require "stackprof"

client = MQTT::Client.new(username: 'testuser', password: 'testpasswd', client_id: "client_#{SecureRandom.hex(10)}", host: '127.0.0.1')
client.connect

Benchmark.ips do |x|
  x.report("send message") {
    i = rand(1..10)
    topic = "to/timebox#{i}/cameras"
    msg = "message #{Time.now} for timebox#{i}"
    client.publish(topic, msg, true, 1)
  }
end
```

Before this patch:

```
$ ruby -I lib thing.rb
Warming up --------------------------------------
        send message     8.000  i/100ms
Calculating -------------------------------------
        send message     85.042  (± 4.7%) i/s -    432.000  in   5.089261s
```

After this patch:

```
$ ruby -I lib thing.rb
Warming up --------------------------------------
        send message   915.000  i/100ms
Calculating -------------------------------------
        send message      9.453k (± 4.5%) i/s -     47.580k in   5.043716s
```

The downside is that the timeout isn't exact.  Since `IO.select` times
out every `0.5` seconds (according to the `SELECT_TIMEOUT` constant),
the deadline in the `publish` method could be missed by that amount of
time.

Refs njh#115
njh pushed a commit that referenced this issue Mar 9, 2020
This commit changes the `publish` method to use a queue to wait for
Puback packets rather than polling a hash.  Every time the read loop
gets data or a timeout from `IO.select`, it will send a message to
everyone waiting for a `Puback` packet.  If the we're within the
deadline, then the loop executes again, if we got a packet, we'll return
the packet, and if we're outside the deadline, a `-1` is returned.

This upside is that this patch speeds up the publish method by over
100x.  Here is the benchmark:

```ruby
require "securerandom"
require "mqtt"
require "benchmark/ips"
require "stackprof"

client = MQTT::Client.new(username: 'testuser', password: 'testpasswd', client_id: "client_#{SecureRandom.hex(10)}", host: '127.0.0.1')
client.connect

Benchmark.ips do |x|
  x.report("send message") {
    i = rand(1..10)
    topic = "to/timebox#{i}/cameras"
    msg = "message #{Time.now} for timebox#{i}"
    client.publish(topic, msg, true, 1)
  }
end
```

Before this patch:

```
$ ruby -I lib thing.rb
Warming up --------------------------------------
        send message     8.000  i/100ms
Calculating -------------------------------------
        send message     85.042  (± 4.7%) i/s -    432.000  in   5.089261s
```

After this patch:

```
$ ruby -I lib thing.rb
Warming up --------------------------------------
        send message   915.000  i/100ms
Calculating -------------------------------------
        send message      9.453k (± 4.5%) i/s -     47.580k in   5.043716s
```

The downside is that the timeout isn't exact.  Since `IO.select` times
out every `0.5` seconds (according to the `SELECT_TIMEOUT` constant),
the deadline in the `publish` method could be missed by that amount of
time.

Refs #115
@njh
Copy link
Owner

njh commented Mar 9, 2020

@rgaufman please can you test to see if @tenderlove's improvements fix your performance problems?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants