原文地址:Is localStorage performance a problem?
如果說2012年對於web開發世界來說有什么值得記住的事的話,關於localStorage性能的爭論一定高居榜首。這場爭論開始於Christian Heilmann寫的一篇文章: There is no simple solution for localStorage(中文版:本地存儲並不簡單)。在這篇文章里,他得出了幾個關於localStorage性能差的幾個論斷。除此之外,他還建議對現有api進行改變以及對於可選api(IndexedDB、webSQL)的優化。
但是這篇文章的讀者並不多,只有很少的文章進行深入分析。John Allsopp寫了一篇叫localStorage perhaps not so harmful的文章,在這篇文章里他分析了通過localStorage讀寫10KB的數據的時間。我也寫了一篇文章來說明我的觀點: localStorage無罪!(In defense of localStorage)。在這篇文章里我比較了相同條件下的localStorage和cookie(和磁盤訪問讀寫的性能差不多)的讀寫性能。我和John Allsopp根據分析得出的結論都認為localStorage的性能問題並沒有嚴重到拋棄使用它的地步。
在和Mozilla的Taras Glek(在他自己的博客上寫了一篇相關主題的文章:PSA: DOM localStorage considered harmful)交流之后,我意識到自己和John Allsopp的測試方法和localStorage真正的運行方式相比是有缺陷的。
localStorage的關鍵問題在於它是通過同步操作的方式來進行文件I/o操作。寫入localStorage的數據都會保存到磁盤上,除非主動刪除數據,否則數據是永遠不會過期的。用過nodeJs的人都知道,對於文件的I/O是非常昂貴和不一致的(不可信賴)。任何時間點任何的程序都可以訪問文件。舉例來說,你注意到過當一個殺毒軟件運行的時候你的電腦是如何慢下來的嗎?在理想狀態下,你讀取的文件不會有其他程序在同一時間訪問該文件。在極端壞的情況下,如果你想讀取一個文件,就必須等待文件上的鎖被釋放(其他程序操作文件時會鎖定文件)。
這就引申出一個瀏覽器的問題:到底什么時候磁盤的數據才應該被讀取?只有兩種可能。第一種,數據可以在頁面加載時就被讀取,這樣可以確保后面的讀取快速操作。當然,這也意味着localStorage將會影響頁面的加載時間,即使localStorage讀取的數據並不會使用。在理想情況下,你並不會注意到這有多大不同。但在極端壞的情況下,這可能會導致頁面加載時間的延長。
第二種方式是在localStorage第一次被使用(JS操作)的時候再從磁盤讀取數據。這樣可以阻止對於頁面加載的中斷,但也意味着在第一次通過localStorage訪問數據的時候瀏覽器會中斷對於頁面的處理(js執行、頁面渲染等)。磁盤文件的所有localStorage數據都會被寫進內存以加快后面對於localStorage數據的讀取速度。同樣地,通過localStorage保存的數據會首先寫入內存,以后再寫入到磁盤文件里,以加快寫的速度。firefox和chrome都使用了第二種方式(opera好像也是如此,我沒有驗證過)。這也導致了通過像jsPerf這樣從不加載頁面的工具來測量localStorage的性能是很難保證准確度的。
現在問題已經很清楚了:第一次通過localStorage.getItem()讀取數據的時間是不可預測的。此外,這是一個阻塞型方法,因此瀏覽器會停止處理頁面直到數據從磁盤中讀出。后面我和John Allsopp的一起合作的測試(第一次慢讀取,后面的快讀取)表明localStorage也不是那么壞。最近,Chromium的工程師William Chan做了一些通過測量第一次讀取時間來判斷localStorage性能的分析。
William的分析結果表明在Windows, Mac, 和Linux上75百分位數(就是說75%的測驗都是很快的,只有25%有點慢)的測驗讀取速度都很快。事實上,在Windows和Linux上,只有99百分位數(99%的測試的首次讀取時間小於1s,只有1%的測試超過1s)的測試的首次讀取時間超過一秒(Mac上仍舊小於1s)。如下圖表所示:
注:百分位數(percentile)的概念參考百度百科http://baike.baidu.com/view/1323573.htm
William Chan’s localStorage Read Performance Numbers
Taras Glek在Google+ comment上說他也分析出firefox也有類似的性能特征。Taras Glek和William的結論都證明了localStorage的性能並不像我們一開始想的那樣低下。
盡管如此,我還是自己做了一些試驗。通過在高級瀏覽器下使用performance.now(),在不支持時使用Date對象的方式來測量localStorage的首次讀取時間和后面的讀取時間。下面是我的一些發現(除非特別聲明,測試環境都是Windows 7):
- 在chrome下,第一次讀取花費大約1ms,后面的讀取花費0ms;
- 在Firefox下,第一次讀取花費大約0.5ms,后面的讀取花費大約0.1ms;
- 在IE9下,第一次讀取和后續的讀取都花費0ms。我不確定IE是如何加載這些數據的,當IE采取的方式應該和其他瀏覽器大不一樣;
- 在opera下,第一次讀取花費大約1ms,后面的讀取花費0ms;
- 在IOS 6下的Safari下,第一次讀取花費了整整24ms,后續的讀取花費0ms。
在上面的結論中有個有趣的現象,safari好像會把所有的數據都保存在內存中,直到應用程序關閉才再次讀取磁盤。只要safari保持運行,數據讀取將繼續只花費0ms。
基於以上結果,我沒有看到在桌面電腦上不使用localStorage的任何理由。雖然99百分位數有些差的數據,但這僅僅是一些極端值。從平均數來看,localStorage的性能在所有瀏覽器下還是表現的很好的,即使是第一次讀取的時候。
在移動設備下還需要更多更深入的研究。在移動設備上對磁盤進行操作比桌面設備更加昂貴,我們應該盡量減少對於磁盤的操作。不過,至少在IOS下這種花費通過只有在每個應用程序session中才讀取一次的方式減輕了很多。由於大多數情況下用戶並不會關閉safari,因此我們應該判斷是否有必要花費初始化讀取數據的時間。如果24ms比從服務器讀取相同資源的時間要少,那么通過localStorage存儲數據的性價比就會很高。
總的來說,我還是覺得對於localSorage性能差的論斷有點草率。最新的數據表明localStorage的性能並不像早期有些文章寫的那樣讓人不敢使用這項技術。磁盤I/O操作雖然一般情況下比較慢,但這是一個很好的例子表明越多的人關注和親身測試對於一些有爭論的觀點對人們錯誤的引導有很大的抑制作用(事實勝於雄辯)。有很多網站都在使用這個api,但並沒有哪家網站公開宣稱localStorage存在性能問題。沒有了性能上的問題,雖然在頁面加載時使用localStorage需要思量再三,但我還是會盡可能的使用localStorage。
補充:在windows 8的IE10下,默認localStorage是不可訪問的。如果我們直接使用localStorage的話,控制台會報Error:拒絕訪問。解決方式如下:Get the Internet Explorer script error which says "Access denied error for LocalStorage"