Last week, I compared Chrome and Firefox spdy/3 implementations. The result was inconclusive because I was using a sample page too far removed from the norm. Tonight, I revisit that comparison.
I again start with an artificial round trip time (RTT) of 100ms:
➜ spdybook-site git:(master) ✗ sudo tc qdisc add dev lo root netem delay 50ms [sudo] password for chris: ➜ spdybook-site git:(master) ✗ ping localhost PING localhost.localdomain (127.0.0.1) 56(84) bytes of data. 64 bytes from localhost.localdomain (127.0.0.1): icmp_req=1 ttl=64 time=100 ms 64 bytes from localhost.localdomain (127.0.0.1): icmp_req=2 ttl=64 time=100 ms 64 bytes from localhost.localdomain (127.0.0.1): icmp_req=3 ttl=64 time=100 ms ...It is not quite realistic, but it is close enough and has the virtue of making math easier.
As I did last night, I use a web page that includes roughly 50 smallish images and other resources (85 is typical):
For each trial, I stop the node-spdy (spdy-v3 branch) server and close the browser before making a connection. Ideally this will make for consistently cold TCP/IP pipes over which the SPDY connections will be made.
I run a regular Chrome connection over spdy/3:
Then a Chrome connection over spdy/3 with spdy server push:
And, lastly Firefox over vanilla spdy/3 (Firefox does not support push):
I had expected to find that the Firefox connection is faster than the vanilla Chrome connection. New in spdy/3 is flow control, which I thought would make the difference. But upon reflection, flow control should not enter into this—none of the images are large enough to threaten SPDY's default receive window (64kb). In other words, there should be no flow control.
So what is the difference? I suspect that the difference is that Firefox is requesting more resources initially, pushing the TCP/IP CWND up faster. This would seem to be born out by virtue of the first large packets in each starting at the 0.7 second mark for Firefox and nearly 1.0 seconds for Chrome. Last night's Speed Tracer diagram would also suggest that Chrome is processing the
<head>items before requesting the images from the
Regardless, I had wanted to compare more realistic spdy/3 implementations. For that, it seems that I need to add a couple of images that push the bounds flow control past the default receive window. I add them to the bottom of the
<body>to get them loading last:
Now I repeat the trials for Chrome with vanilla spdy/3:
Chrome with spdy/3 and spdy server push:
And, lastly, Firefox over spdy/3:
This is what I had expected: Firefox trounces Chrome's flow control implementation. With a SPDY receive window of 256mb, Firefox effectively eschews spdy/3 flow control. The SPDY server is free to jam as much data as it wants on the wire. In this case, it completely fills the TCP/IP receive window (the top lines in the graphs).
With Chrome, on the other hand, the SPDY server is forced to wait for WINDOW_UPDATE frames before it can send back the next batch of data. Since Chrome never updates the SPDY receive window by more than 32kb, the server is unable to send enough data to push the bounds of the TCP/IP receive window.
Chrome's current implementation seems clearly sub-optimal. In spdy/2 (and in vanilla HTTP), Chrome is more than capable of handling large TCP/IP receive windows. There is no reason for Chrome to continue to request WINDOW_UDPATES of 32kb—instead Chrome should be asking for near the TCP/IP receive window's worth of data.
That said, from the server's point of view, this implementation might be regarded as beneficial. The server is able to jam the smallish resources back to the browser in short order. The larger items, which the user would likely not expect to render immediately anyway, can be sent back at a more leisurely pace—freeing up the server to respond to other SPDY sessions.
Regardless, it is not the responsibility of the client to throttle the server's response like this. The server is more than capable of doing it on its own. So I expect that Chrome's WINDOW_UPDATE algorithm will get a little more sophisticated in the near future.