WebSockets performance

Client-server architecture is a popular answer for multi-user, remote applications. Comparing with single-user programs working on the local machine, this architecture always has worse responsiveness. Today's world of multiuser, remote application is dominated by web technology. When starting a new project one could think "Wouldn't it be better to choose the WebSocket protocol to speed up client-server communication?". This document presents the results of tests, that have been performed to check if sending data through WebSocket is faster then over HTTP and if there is a performance profit of establishing connection only once for a user session.

Tests specification

The testing scenario was comparing the effectiveness of sending a various amount of data through HTTP and WebSocket RFC 6455.

The tests were executed in four environments:

Each test was run twice in each environment and then a better result was chosen.

Software:

Test cases:

Y-axis presents time in seconds. The smaller, the better.

Looking at the charts from the Internet application point of view (WAN and HSDPA+), WebSocket transmission is faster than HTTP but only for data smaller than 100 KB. Considering Intranet application (LAN) we can see, that only sending one char was noticeably faster in WebSocket technology.

Let's look at how is it doing when sending a larger package of data, 10 MB:

In all cases sending 10 MB data via WebSocket is extremely ineffective.

More information about the effectiveness of protocols can be given by the localhost test, in which network delays are eliminated.

Investigation

The localhost chart gave us a cue, that the problem probably lies in WebSocket protocol itself.

In such a case, let's examine the sending function:

Profiling websocekt-client send() function:


   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    4.550    4.550 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 websocket.py:256(__init__)
        1    0.000    0.000    0.000    0.000 websocket.py:276(create_frame)
        1    0.011    0.011    4.512    4.512 websocket.py:292(format)
        5    0.000    0.000    0.000    0.000 websocket.py:296(<genexpr>)
        1    0.009    0.009    4.500    4.500 websocket.py:323(_get_masked)
        1    4.480    4.480    4.491    4.491 websocket.py:327(mask)           (!)
        1    0.011    0.011    4.550    4.550 websocket.py:549(send)
        1    0.000    0.000    0.027    0.027 websocket.py:728(_send)
        1    0.000    0.000    0.000    0.000 {built-in method any}
        1    0.000    0.000    4.550    4.550 {built-in method exec}
        2    0.000    0.000    0.000    0.000 {built-in method isinstance}
        3    0.000    0.000    0.000    0.000 {built-in method len}
        1    0.000    0.000    0.000    0.000 {built-in method pack}
        1    0.000    0.000    0.000    0.000 {built-in method urandom}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.027    0.027    0.027    0.027 {method 'send' of '_socket.socket' objects}
        1    0.010    0.010    0.010    0.010 {method 'tobytes' of 'array.array' objects}

According to the Websocket protocol RFC 6455 :

6.1 5. If the data is being sent by the client, the frame(s) MUST be masked as defined in Section 5.3.

This is done to prevent cache poisoning on the proxy.

Conclusion

Taking aside the profits of bidirectional communication, WebSocket could be a good solution for an Internet application, where clients send small data, very often to the server. For sending larger data it would be a good idea to create a dedicated, non-WebSocket channel.

For the Intranet application, it would be probably better to stay with HTTP technology. Architecture would be easier, limited only to one channel.

Resources