When network is faster than browser cache (2020)
simonhearne.com
simonhearne.com
In ~2010, I was benchmarking Solarflare (Xilinx/AMD now) cards and their OpenOnload kernel-bypass network stack. The results showed that two well-tuned systems could communicate faster (lower latency) than two CPU sockets within the same server that had to wait for the kernel to get involved (standard network stack). It was really illuminating and I started re-architecting based on that result.
Backing out some of that history... in ~2008, we started using FPGAs to handle specific network loads (US equity market data). It was exotic and a lot of work, but it significantly benefited that use case, both because of DMA to user-land and its filtering capabilities.
At that time our network was all 1 Gigabit. Soon thereafter, exchanges started offering 10G handoffs, so we upgraded our entire infrastructure to 10G cut-through switches (Arista) and 10G NICs (Myricom). This performed much better than the 1G FPGA and dramatically improved our entire infrastructure.
We then ported our market data feed handler's to Myricom's user-space network stack, because loads were continually increasing and the trading world was continually more competitive... and again we had a narrow solution (this time in software) to a challenging problem.
Then about a year later, Solarflare and it's kernel-compatible OpenOnload arrived and we could then apply the power of kernel bypass to our entire infrastructure.
After that, the industry returned to FPGAs again with 10G PHY and tons of space to put whole strategies... although I was never involved with that next generation of trading tech.
I personally stayed with OpenOnload for all sorts of workloads, growing to use it with containerization and web stacks (Redis, Nginx). Nowadays you can use OpenOnload with XDP; again a narrow technology grows to fit broad applicability.
We architected our applications around that as well. The firm continued on to the FPGA path though that was after my time there.
I do still pick up SolarFlare cards off of eBay for home lab purposes.
[1] https://gist.github.com/neomantra/3c9b89887d19be6fa5708bf401...
A few years ago I met with a cloud computing company who was following similar techniques to reduce the noisy neighbor problem and increase performance consistency.
Frankly, it’s good to see that there still are bare metal performance specialists out there doing their thing.
That said, I have had operational issues in migrating to Docker, which is another sort of performance impact! I reference some of my core isolation issues in that Redis gist and this GitHub issue [2].
[1] https://www.redhat.com/cms/managed-files/201504-onload_conta... [2] https://github.com/moby/moby/issues/31086#issuecomment-30374...
Processes communicating on different systems had actual parallelism, because they each had their own network device. Processes communicating on the same system were essentially serialized, because they were competing with each other for the same pseudo-device. At the time, Linux kernel developers basically said "Yeah, don't do that" when people brought up the performance problem.
This a real old example of this technique using boost::asio [1]. We adapted that from the one in our own reactor library to Onload-pipe-accelerate some OSS that used ASIO.
> Just the time it takes to correctly construct an ethernet frame is not negligible.
For that reason, Onload/TCPDirect (and other vendors too) have APIs that allow you to pre-allocate and prepare packets (headers and payload), then you can do some final tweaks -- like choosing a ticker and price in an order packet -- and then send it out with minimal latency.
I am always bemused when people imagine that communication over localhost involves packets hitting an ethernet adapter.
It has been many years since i tested this but unix sockets seemed slower to me than loopback TCP even
If you need real speed you should use UDP/MC, or I have used a lot of commercial middlewares which used "shared memory" transports. i.e. messaging paradigm APIs wrapping various configurable messaging middlewares to provide a fairly unified interface to the application messaging concepts.
They're essentially the same... except they're not and there's a boatload of work in the middle that doesn't need to be done for unix domain sockets.
I can imagine some edge-cases however, where differences in stacks and configuration (in/out buffers vs. shared buffers, buffer sizes, packet loss vs. reliable, locking, blocking vs. non-blocking writes) may lead to more optimal scheduling under concurrent workloads. As soon as you have more than one thread doing something, there's a boatload of moving parts to consider.
But if you have everything properly tuned, unix domain sockets should win every time. If your results said something else, you probably weren't benchmarking what you thought you were benchmarking.
The kind of offload most prevalent on consumer routers is NAT performed by the Ethernet switch ASIC, which I don't think can be fairly described as an even smaller CPU running a proprietary network stack—you might have a microcontroller-class CPU core in the switch chip, but there's a separation of control and data planes.
> It turns out that Chrome actively throttles requests, including those to cached resources, to reduce I/O contention. This generally improves performance, but will mean that pages with a large number of cached resources will see a slower retrieval time for each resource.
I can understand throttling network requests, but disk requests? The only reason to do that would be for power savings (you don't want to push the CPU into a higher state as it loads up the data).
I do. Which is why it's silly to throttle the cache IO.
The read latency for disks is measured in microseconds. Is it possible for the server to be able to respond faster? Sure. However, if you aren't within 20 miles of the server then I can't see how it could be faster (speed of light and everything).
These design considerations will depend greatly on where you are at. It MIGHT be the case that eschewing a client cache in a server to server talk is the right move because your servers are likely physically close together. That'd mean the server can do a better job making multiple client requests faster through caching saving the memory/disk space required for the clients.
There is also the power consideration. It take a lot more power for a cell phone to handle a network request than it does to handle a disk request. Shouting into the ether isn't cheap.
It’s an interesting approach because it removes the assumption browser caches mask volume for infrastructure purposes rather than user latency. I think you can overwhelm costs for low or no income services. If this is widely adopted we ask the providers of our servers to pay for that latency improvement.
But the active tab when browser window is an active one should work without any throttling for lower response time.
The cost is the cache isn’t masking the remote infrastructures true demand. But there’s no spec that says it must afaik. I think the most interesting angle on this is that - infrastructure users serving data are paying for this latency optimization. Is that right? My view is this is the concerning thing - if widely adopted it could materially increase the cost to service providers some of whom operate on thin or negative margins, or with no income at all.
> It is about "network" vs "the (usually antiquated) file-backed database your browser calls a cache."
It's actually nothing to do with the design of the cache itself as far as I can tell. If you finish reading the article you'll see that it's about a throttling behavior that interacts poorly with the common optimization advice for HTTP 1.1+, exposed by caching.
> The latter is kind of self-evident: the browser cache is there to save bandwidth, not to be faster.
I don't think that's something you can just state definitively. I suspect most people do in fact view the cache as an optimization for latency. Especially since right at the start of the article, the first sentence, the "race the cache" optimization is introduced - an optimization that is clearly for latency and not bandwidth purposes.
Edit: I didn't need this post to tell me about the "waiting for cache" message I used to see with Chrome.
The latter is a well known and documented phenomenon. The moment popular drives are consistently faster than network again, expect to see a bunch of people writing articles about using local or disk cache, recycling old arguments from two decades ago.
When you throttle requests for IO then the latency is proportional to the duration of all of the previous queries.
If you have a lot of boxes running your application, sometimes it's nice to be able to do an expensive thing once per expiry period across the cluster, not once per expiry per app server. Or sometimes you want a cheap simple way to invalidate or write a new value for the full cluster.
The throughput and latency of SSDs does make them a workable cache medium for some uses, with much lower cost/GB than RAM, but that can be independent of the local/remote choice.
CPU caches are such a common thing to think about now that we have associated the word "cache" with latency improvements, since that is one of the most obvious benefits of CPU caches. It is not a required feature of caches in general, though.
The browser cache was built for a time when bandwidth was expensive, often paid per byte, the WAN had high latency, and disk was cheap (but slow). I don't know exactly when the browser cache was invented, but it was exempted from the DMCA in 1998. Today, bandwidth is cheap and internet latency is a lot lower than it used to be. From first principles, it makes sense that the browser cache, designed to save you bandwidth, does not help your website's latency.
Edit: In light of the changes in the characteristics of computers and the web, this article seems to mainly be an argument for disabling caching on high-bandwidth links on the browser side, rather than suggesting "performance optimizations" that might silently cost your customers on low-bandwidth links money.
Cache is "slow" because the number of ongoing requests-- including to cache! --are throttled by the browser. I.e the cache isn't slow, but reading it is waited upon and the network request might be ahead in the queue.
And as the article points out, the cache is in fact lower latency and does improve latency for a single request. The issue is with throttling.
The article is absolutely not suggesting disabling caching, nor does any information in the article indicate that that would be a good idea. Even with the issue pointed out by the article the cache is still lower latency for the majority of requests.
The browser cache is kind of like a database and also tends to hold a lot of cold files, so it may take multiple seeks to retrieve one of them. Apparently it has throughput problems too, thanks to some bottlenecks that are created by the abstractions involved.
Firefox uses SQLite for on-disk cache backend. Not the latest buzzword, but not exactly antiquated. I expect a cache backed in Chrome to be at least as fast.
> cache is there to save bandwidth, not to be faster
In most cases a cache saves bandwidth and reduces page load time at the same time. Internet connection which is faster than a local SSD/HDD is a rare case.
They were already partitioned by the requested eTLD+1, because they were already partitioned by the requested URL, which contains the requested eTLD+1. Chrome's change was additionally partitioning it by 2 more eTLD+1s: the top-level eTLD+1 and the frame eTLD+1. It looks like Firefox's change was to add 1 eTLD+1 to the original behavior: the top-level eTLD+1.
Did I miss a follow up on this, or did it remain unanswered as to what the benefit of racing against the network is?
The post basically says that sometimes the cache is slower because of throttling or bugs, but mostly bugs.
Why is Firefox sending an extra request instead of figuring out what is slowing down the cache? It seems like an overly expensive mitigation…
This is a very weird comment, considering that a) it's cheaper than yesteryear but SATA SDD (or even modern magnetic HDDs) are still sold and are in active use and b) ignores phones completely, where a large number of sites would have mobile-dominated visitors and can't just switch to an NVMe-like performance even for those with large disposable incomes (because at the end of the day even with UFS phones are still slower than NVMe latency-wise).
OP mentioned specifically that “there have been bugs in Chromium with request prioritisation, where cached resources were delayed while the browser fetched higher priority requests over the network” and that “Chrome actively throttles requests, including those to cached resources, to reduce I/O contention”. I wonder if there are also other limitations with how browsers retrieve from cache.
Yes that is the subject of this post.
https://simonhearne.com/2020/network-faster-than-cache/#the-...
Of course 200 microseconds still isn't fast. That translates to serving 5000 requests per second, leaving the CPU almost completely idle.
Another odd fact was that we in fact did have to implement our own semaphores and throttling to limit concurrent reads from SSDs.
I guess this latter part refers to the trade-off between compiling all your assets into a single file, and then requiring clients to re-download the entire bundle if you change a single CSS color. The other extreme is to not bundle anything (which, I gather from the article, is the standard practice since all major browsers support HTTP/2) but this leads to the described issue.
What about aggressively bundling, but also keeping track at compile time of diffs between historical bundles and the new bundle? Re-connecting clients could grab a manifest that names the newest mega-bundle as well as a map from historical versions to the patches needed to bring them up to date. A lot more work on the server side but maybe it could be a good compromise?
Of course that's the easy version, but it has a huge flaw which is all first-time clients have to download the entire huge mega bundle before the browser can render anything, so to make it workable it would have to compile things into a few bootstrap stages instead of a single mega-bundle.
I am clearly not a frontend dev. If you're going to throw tomatoes please also tell me why ;)
* edit: found the repo that made me think of this idea, https://github.com/msolo/msolo/blob/master/vfl/vfl.py but it's from antiquity and probably predates node and babel/webpack, but the idea is you name individual asset versions with a SHA or tag and let them be cached forever, and to update the web app you just change a reference in the root to make clients download a different resource dependency tree root (and they re-use unchanged ones) *
Seems like a huge pain to think about tbh. Seems like part of the problem would be solved by compiling everything into a single file that supported streaming execution.
https://blog.chromium.org/2009/07/smaller-is-faster-and-safe...
You can have a "vendor" chunk (or a few) which just holds all 3rd party dependencies, a "core components" chunk which holds components which are likely used on most pages, and then individual chunks for the rest of the app broken down by page or something.
It speeds up compilation, gives better caching, no need for a stateful mapping file outside of the HTML file loaded (which is kind of the point of the <link> tag anyway!), and has lots of knobs to tune if you want to really squeeze out the best load times.
When I first saw this, I was confused why Linux (and Ubuntu and Debian) performed so poorly. But then I asked myself; is the data even representable? Or is Linux worse because users on Linux tend to stick to lower end hardware, since the measurenents were, AFAIR, taken for granted from website visitors instead of being tested on same hardware with different operating systems installed?
I don't know, can someone explain the discrepancy?
Is the raw data available? What is the number of requests that were included in the measurements for Debian (that doesn't hit cache in the first 5 ms)?
Thanks!
[0] "nbd" / https://en.wikipedia.org/wiki/Network_block_device ; this driver is of course still in the kernel; you could do this today with an anemic raspberry pi if you wanted
Then again, whats a TB worth on just one xeon server? Probably cheaper... or not?
https://docs.google.com/document/d/1Aa7OKFRdtmn4IFzgHYfqeqk5...
Sun was right.
Tl;dr: cache is "slow" because the number of ongoing requests-- including to cache! --are throttled by the browser. I.e the cache isn't slow, but reading it is waited upon and the network request might be ahead in the queue.
No good solutions either, weird throttling isn't feasible when creating Web apps.
There's a video and a paper linked on this page with the information on this absurdity.