If 2012 will be remembered for anything specific in the web development world, the debate over performance issues with
localStorage will surely be high on the list. The debate began with a post by Christian Heilmann entitled, There is no simple solution for localStorage, in which he made several claims about poor
localStorage performance and called for changes to the existing API or the development of an alternative API.
The problem with the article was that it didn’t have any numbers, so that set off a large number of follow-up articles taking up the task of providing numbers. John Allsopp wrote localStorage perhaps not so harmful, in which he measured the amount of time it took to read and write 10KB using
localStorage. I also entered the mix with an my article, In defense of localStorage, in which I compared the read and write performance of
localStorage to that of cookies (which have similar performance characteristics due to disk access). We both determined that
localStorage didn’t seem slow enough to warrant throwing it away.
After catching up with Taras Glek from Mozilla (who wrote his own post on the subject, PSA: DOM localStorage considered harmful), I realized that both John’s benchmark and my own were flawed in their approach due to the way that
localStorage actually works.
The key issue with
localStorage is that it’s a synchronous operation that does file I/O. All of the data stored in
localStorage is written to disk in order to persist across sessions. As anyone who has worked with Node.js realizes, file I/O is expensive and inconsistently so. There are any number of processes that could be accessing files at any point in time. For example, have you ever noticed how your computer slows down while an antivirus program is run? In a perfect world, there is no other process trying to access the file at the same time that you are in the read happens quickly. In the worst case, you have to wait for a lock to be released on the file before it can be read.
This introduces a problem for browsers: when should the data be read from disk? There are really only two options. First, the data could be read during page load to make sure that all subsequent read operations are fast. Of course, that would mean that
localStorage would affect page load time even if it’s never used during the page lifecycle. In the perfect case, you wouldn’t notice much of a difference, but in the worst case, you could be adding milliseconds (or seconds) to the page load time.
The second approach is to wait until
localStorage is accessed for the first time and then read the data from disk. This prevents any interruption of page load time but does mean that the browser will freeze when the first read happens. All of the data in the file is read into memory so each subsequent read from
localStorage is fast. Likewise, writes our first stored into memory and then written to disk later so that is fast. Both Firefox and Chrome take this approach (Opera appears to as well, but I haven’t confirmed), which makes it hard to measure
localStorage performance using a tool like jsPerf that never loads the page.
So the issue is clear: the time it takes for the first called to
localStorage.getItem() is unpredictable. Additionally, it is a blocking call, so the browser freezes while waiting for data to be read from disk. The benchmarks that John and I used grouped together the first, slow read with all of the subsequent fast reads, so our numbers don’t really mean much. Recently, Chromium engineer William Chan did some analysis on
localStorage performance measuring the first read.
Williams findings showed that reads were very fast through the 75th percentile on Windows, Mac, and Linux. In fact, it was only in the 99th percentile that the initial read took longer than a second on Windows and Linux (still less than a second on Mac). See the figure below.
Taras noted in a Google+ comment that he saw a similar performance characteristics and Firefox. So it seems like Taras and William have proven that the performance issues around
localStorage aren’t as dire as first thought.
Still, I decided to do some testing on my own. I measured the first read from
localStorage and then subsequent reads using
performance.now() if available and
Date objects otherwise. Here’s what I found (on Windows 7 unless otherwise noted):
- On Chrome, the first read takes ~1ms and subsequent reads take 0ms.
- On Firefox, the first read takes ~0.5ms and subsequent reads take ~0.1ms.
- On Internet Explorer 9, all reads take 0ms. I’m not sure how Internet Explorer is loading this data, but it doesn’t appear to be the same as with other browsers.
- On Opera, first read takes ~1ms and subsequent reads take 0ms.
- For Safari on iOS 6, the very first read takes up to 24ms and subsequent reads take 0ms. The interesting thing here is that Safari appears to keep everything in memory and never reads from disk again until the application is shut down. As long as Safari remains running, reads continue to take 0ms.
At this point, I don’t see any reason not to use
localStorage on the desktop. Yes, there are some poor numbers in the 99th percentile, but there will always be outliers to the norm. On average, performance seems pretty good across desktop browsers even on that initial read.
For mobile, there’s more research needed. Clearly disk operations on a mobile device are going to be more expensive than on the desktop and you should try to limit those as much as possible. However, at least iOS is somewhat mitigating the cost by only doing it once per application session. Since most users won’t be shutting down Safari on a regular basis, you need to decide if it’s worth taking the hit for that initial read or not. If 24 ms is faster than what it would take to get the same resource from the server, then it may well be worth the hit to store that data in
Overall, I still feel that the cries of bad
localStorage performance were premature. The data that is now available indicates that performance isn’t as horrible as some of the early blog posts posited. Yes, disk I/O always has the potential to be slow, but I think this was a good example of why it’s important to get actual numbers before warning people away from what has arguably become one of the most popular HTML5 APIs. There are still a lot of sites relying on this functionality and none that have come out publicly to say that
localStorage performance was a problem. I know I will still use it without worrying about performance issues, although I will definitely think twice before using it during page load.