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

Write directly to the Stream #6

Merged
merged 4 commits into from
Oct 21, 2015
Merged

Write directly to the Stream #6

merged 4 commits into from
Oct 21, 2015

Conversation

mcollina
Copy link
Member

I have done some in-deep performance analysis on this library and MQTT.js, and I was able to squeeze roughly a 5x throughput improvement over a TCP socket, by running slower code that allocates less memory.

On this library, using the old generate:

Total time 4958
Total packets 1000000
Packet/s 201694.2315449778

On this library, using writeToStream:

Total time 3062
Total packets 1000000
Packet/s 326583.9320705421

Which is roughly 60% faster than the old generate().

On my system, I was able to ramp up the QoS 0 throughput on MQTT.js from 40k msg/s to 200k msg/s on node v4.2.0 using the stream.cork() and stream.uncork() technique (PR to follow).

This pull requests introduces that logic and removes the full packet allocation and subsequent buffer.write*() and buffer.copy() calls. Most of the contents of a packet are only allocated once and cached forever. Even the lengthy algorithm for calculating a length is memoized. On a new PUBLISH packet, the only things that are allocated are the topic and the payload.

All this has a downside: the old generate() method is 50% slower.

I propose to bump the MAJOR for this, as it changes a lot, and it might broke existing code. Not sure about MQTT.js, but we'll discuss it there.

I would like if some of you review this code: @adamvr @pmorjan @psorowka @sandro-k @xadh00m @mdickens @kokeksibir

Thanks to @trevnorris for the discussion at nodeconf.eu, and to @thlorenz for cpuprofilify which validated @trevnorris intuition.

@sandro-k
Copy link
Contributor

👍 but I will need some time to look at this..

@mcollina
Copy link
Member Author

@sandro-k there is some heavy caching of binary representation of numbers and packet length that might cause problems in the Browser. If you can check it, it would be awesome. I'm planning to get this merged & released by the end of the week, so we can start upgrading MQTT.js after (I already have the code ready). Anyway, there will be time for later fixes if you can't review.

@sandro-k
Copy link
Contributor

just make a RC and we will see from there

, numCache = require('./numbers')

function generate(packet, stream) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you have left cork/uncork calls out of writeStream because of backwards compatibility, right? Even though I'd like to have a short-hand alternative that implicitly does cork/uncork maybe something like,

function alternative_generate(packet, stream) {
    stream.cork();
    generate(packet, stream);
    process.nextTick(uncork, stream);
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, the main reason I've left it out is for backward compatibility, and because I would like the other modules using this to do their cork/uncork as they see fit. Possibly the process.nextTick has some disadvantages that I am unaware off.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commenting w/ partial context, but using nextTick() is how we uncork() in the 'http' module.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trevnorris thanks so much for providing context here! Would you say that using nextTick() is the best way for using uncork()? If so, I think we can just put this in and let the users of this module forget about it.

I would also protect it for backward compatibility:

    if (stream.cork) {
     stream.cork();
     process.nextTick(uncork, stream);
    }
    generate(packet, stream);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is if you're trying to automate it for your users.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @trevnorris! I added this to the PR then!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note that you can't collide calls. Meaning, if a user called cork/uncork inside your own it won't cause any problems with yours.

@kokeksibir
Copy link

Nice to hear 5x performance improvement, it does not happen all the time :) I just reviewed, it's OK for me, I dropped a small comment, though it is just a nice-to-have suggestion and may be unnecessary at all.

@mcollina
Copy link
Member Author

Great! I'll get this merged and I'll get a release candidate out of the door, so we can work on MQTT.js.

@mcollina
Copy link
Member Author

I'll release this as 4.0.0 in the next couple of days. If we need browser-specific fixes, we can always release 4.1.

mcollina added a commit that referenced this pull request Oct 21, 2015
Write directly to the Stream
@mcollina mcollina merged commit c1ebbbd into master Oct 21, 2015
@mcollina mcollina deleted the writeToStream branch October 21, 2015 16:58
@mcollina
Copy link
Member Author

Released as 4.0.0. We'll bump the MINOR if we need to change things for the browser.

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

Successfully merging this pull request may close these issues.

4 participants