Small performance tweak for Orbited 0.6

3 views
Skip to first unread message

sunetos

unread,
Aug 16, 2008, 10:01:40 PM8/16/08
to Orbited Discussion
Hello all! I'm really excited about Orbited, and I've been working on
a pretty complex web app using 0.5-0.6. I was doing some Firebug
profiling of my javascript performance, and I noticed that Orbited
functions were showing up pretty high in the CPU usage list (probably
due to the volume of socket data in my app). I picked the function
that was using the most CPU time, pad6() (used in UTF8.decode()), and
optimized it a bit. My version seems to perform from 2x to 6x as fast
depending on the browser and what else is in that page load:

// Hopefully this gets patched into Orbited. Note that keeping
'000000' hardcoded makes
// a huge difference. Moving it into a variable slowed it down by
50%.
function pad6(str) {
return (str.length < 6)
? ('000000'.substr(0, 6 - str.length) + str)
: str;
}

// The old one from Orbited.js line 2105
function pad6(str) {
while(str.length < 6) { str = "0" + str; } return str;
}

My app uses a substantial amount of socket data, and I'm very
impressed with how well Orbited handles it. I suspect that my app is
testing performance limits much more than most, and I will gladly
contribute more javascript optimizations like this one if you guys are
able to accept community contributions easily right now.

Adam

Jacob Rus

unread,
Aug 16, 2008, 11:42:01 PM8/16/08
to orbite...@googlegroups.com
sunetos wrote:
> Hello all! I'm really excited about Orbited, and I've been working on
> a pretty complex web app using 0.5-0.6. I was doing some Firebug
> profiling of my javascript performance, and I noticed that Orbited
> functions were showing up pretty high in the CPU usage list (probably
> due to the volume of socket data in my app). I picked the function
> that was using the most CPU time, pad6() (used in UTF8.decode()), and
> optimized it a bit. My version seems to perform from 2x to 6x as fast
> depending on the browser and what else is in that page load:
>
> // Hopefully this gets patched into Orbited. Note that keeping

Great! We could definitely use some profiling and optimization of
Orbited's bottlenecks. So far the emphasis has been on making it work
properly, so there is surely quite a bit of low-hanging fruit, like
that example you found. We've also just started building up a test
suite, to make sure that all the edge cases work as they should, and
we don't accidentally introduce regressions. Both of these (tests and
optimization) will hopefully continue to improve as Orbited matures,
given that the concept and API of Orbited should be fairly stable post
0.5.

> My app uses a substantial amount of socket data, and I'm very
> impressed with how well Orbited handles it. I suspect that my app is
> testing performance limits much more than most, and I will gladly
> contribute more javascript optimizations like this one if you guys are
> able to accept community contributions easily right now.

Glad to hear it! We definitely want to accept your contributions. I
think there's a CLA somewhere (I'll let Michael answer about that),
and we'd be glad to give you commit access to the repository if you
want to contributed more than a couple patches.

If you don't mind my asking, what kind of substantial amount of socket
data are you sending around?

Cheers,
Jacob

sunetos

unread,
Aug 17, 2008, 12:29:30 AM8/17/08
to Orbited Discussion

> We've also just started building up a test
> suite, to make sure that all the edge cases work as they should, and
> we don't accidentally introduce regressions.  Both of these (tests and
> optimization) will hopefully continue to improve as Orbited matures,
> given that the concept and API of Orbited should be fairly stable post
> 0.5.

An included set of test scripts would be great; I would actually
prefer to regression-test before submitting a patch.

> Glad to hear it!  We definitely want to accept your contributions.  I
> think there's a CLA somewhere (I'll let Michael answer about that),
> and we'd be glad to give you commit access to the repository if you
> want to contributed more than a couple patches.

Great! I'm glad the project's already at the point where you can
handle contributions.

> If you don't mind my asking, what kind of substantial amount of socket
> data are you sending around?

I don't mind at all. It's related to TCP socket-based online games,
and depending on the situation, a client will receive from 0.5KB/sec
in UTF8 data to 50KB/sec. I haven't tested a large bandwidth case in
Orbited yet, but it ran a constant stream of 5KB/sec for around 90
minutes, and also handled 5 concurrent connections with no hiccups.
These are just rough numbers; I haven't done a benchmark or profile of
anything but the javascript so far.

Michael Carter

unread,
Aug 17, 2008, 2:49:05 AM8/17/08
to orbite...@googlegroups.com
I came across this Unicode encoder/decoder: http://www.webtoolkit.info/javascript-utf8.html -- I have two outstanding questions about it though.

1) is it faster / how much faster than the current version
2) What the heck is the license?

I think it may be significantly faster than our current version, but I don't know.

-Michael Carter

Rui Lopes

unread,
Aug 17, 2008, 9:28:52 AM8/17/08
to orbite...@googlegroups.com
Hi guys,

Michael Carter wrote:
> I came across this Unicode encoder/decoder:
> http://www.webtoolkit.info/javascript-utf8.html -- I have two
> outstanding questions about it though.
>
> 1) is it faster / how much faster than the current version
> 2) What the heck is the license?
>
> I think it may be significantly faster than our current version, but I
> don't know.

I've added a simple unit test (might be flawed, please do check!) at
http://orbited.org/changeset/523 by using native browser functions
(unescape, encodeURIComponent, etc; I remembered this from [1]) to
implementation encode/decode, it seems to be significant faster than
what we currently have, eg: for encoding:

ff3: 21 vs 4 ; 28 vs 3 (500 byte ascii string); 55 vs 7 (1000 byte ascii
string).
sf3: 27 vs 10; 29 vs 8 (500 byte ascii string); 57 vs 15 (1000 byte
ascii string).
ie7: 123 vs 17; 1421 vs 11 (500 byte ascii string); timeout vs 21 (1000
byte ascii string).

We can directly use the encode function, but not the decode (orbited
needs to handle partial utf-8 strings when using a non-binary socket).

Feel free to improve those tests and send patches!

Best regards,
Rui Lopes

[1]
http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html

sunetos

unread,
Aug 17, 2008, 12:07:32 PM8/17/08
to Orbited Discussion
> We can directly use the encode function, but not the decode (orbited
> needs to handle partial utf-8 strings when using a non-binary socket).

I actually had a question about this one (data framing). To handle
partial strings (my data is newline-delimited), my onread handler
looks like this:

, leftoverBuffer : ''
, onRead : function(self, data) {
data = self.leftoverBuffer + data;
var lastCR = data.lastIndexOf('\r\n');
if (lastCR < (data.length - 2)) {
self.leftoverBuffer = data.substr(lastCR + 2);
data = data.substr(0, lastCR);
} else {
self.leftoverBuffer = '';
}

self.onDataBlob(data);
}

It would be great for performance if I didn't have to do that part in
javascript, but instead had a config option in the daemon to auto-
frame the data based on the given delimiter. That way Orbited gives
the javascript a valid data set every time. Would that also allow use
of the browser's decode, or did I misunderstand the context of your
partial string problem?

sunetos

unread,
Aug 17, 2008, 12:39:51 PM8/17/08
to Orbited Discussion
On second thought, it would be more useful if it weren't a per-launch
daemon option, but an option per javascript TCPSocket instance, which
got sent to the daemon for that session along with host, port, etc.
My python is good enough, but I've never really used Twisted; I
wouldn't mind trying to add the feature as a proof of concept. I
guess it would either be handled manually in dataReceived() in
FakeTCPTransport in cometsession.py, or see if Twisted's builtin
LineReceiver (which seems to let you choose the delimiter
http://twistedmatrix.com/documents/8.1.0/api/twisted.protocols.basic.LineReceiver.html
) would work for it. My fear with LineReceiver is that it would be
difficult to know when a batch was done for sending the batch to
javascript. New constructor idea:

var connection = new Orbited.TCPSocket();
connection.onopen = onConnect;
connection.onclose = onClose;
connection.onread = onRead;
connection.open(host, port, false, '\r\n');
// or the object-options style: connection.open(host, port, { binary:
false, lineDelimiter: '\r\n' });

I guess I should probably wait to make sure you guys don't think it's
insane or protocol-breaking before running off with this idea
further ;).

Rui Lopes

unread,
Aug 17, 2008, 1:36:49 PM8/17/08
to orbite...@googlegroups.com
sunetos wrote:
>> We can directly use the encode function, but not the decode (orbited
>> needs to handle partial utf-8 strings when using a non-binary socket).
>>
>
> I actually had a question about this one (data framing). To handle
> partial strings (my data is newline-delimited), my onread handler
> looks like this:
>
> , leftoverBuffer : ''
> , onRead : function(self, data) {
> data = self.leftoverBuffer + data;
> var lastCR = data.lastIndexOf('\r\n');
> if (lastCR < (data.length - 2)) {
> self.leftoverBuffer = data.substr(lastCR + 2);
> data = data.substr(0, lastCR);
> } else {
> self.leftoverBuffer = '';
> }
>
> self.onDataBlob(data);
> }
>
There is something like that in the stomp client:

http://js.io/trac/browser/trunk/protocols/stomp/stomp.js?rev=31#L56

Maybe you can help improve it?

> It would be great for performance if I didn't have to do that part in
> javascript, but instead had a config option in the daemon to auto-
> frame the data based on the given delimiter. That way Orbited gives
> the javascript a valid data set every time.

It could be done in the daemon, but I'm not sure about the perf.
improvement, eg. if it has some impact on the server, I rather do it on
the client, because these days the normal desktop client is quite
powerful (and there are more clients then servers).

Fell free to come up with profiling prof and patches! :-)


> Would that also allow use
> of the browser's decode, or did I misunderstand the context of your
> partial string problem?
>

You understood it correctly, and that would work.

Just to make sure, the problem we want to avoid is the case where a
single unicode code point is encoded as more than one byte, which is not
necessarily the same problem as splitting the input at line boundaries;
because a single line might end up in more than one call to onread,
where the unicode code point is half in the first onread and the rest on
other onread's (using the browser decode we don't seem to be able to
detect these cases).


Best regards,
Rui Lopes

Adrian Weisberg

unread,
Aug 17, 2008, 4:35:40 PM8/17/08
to Orbited Discussion

> My app uses a substantial amount of socket data, and I'm very
> impressed with how well Orbited handles it. I suspect that my app is
> testing performance limits much more than most, and I will gladly
> contribute more javascript optimizations like this one if you guys are
> able to accept community contributions easily right now.
>
> Adam

We do have a CLA. I'm about to put a blog post on it up, and I'll send
you a copy shortly.

-Adrian

sunetos

unread,
Aug 21, 2008, 12:37:23 AM8/21/08
to Orbited Discussion
Excellent! My project is a sub-project of a rather large one, so I
need to wait until our lawyers approve the CLA. Once done I'll email
it in and start profiling client vs. server unicode optimizations.
Reply all
Reply to author
Forward
0 new messages