localStorage 的速度/成本

Posted

技术标签:

【中文标题】localStorage 的速度/成本【英文标题】:Speed/cost of localStorage 【发布时间】:2011-12-25 19:55:20 【问题描述】:

使用 javascript 在 localStorage 中查找值的速度有多快?

是否有任何性能测试的链接表明是否值得在 JavaScript 对象中缓存数据?或者浏览器是否已经缓存了从 localStorage 访问的值?

我对 localStorage 的 Firefox 和 Chrome 实现特别感兴趣。

【问题讨论】:

A performance test-case 显示什么? (写一个,我会为你在几个浏览器上运行它;-) 【参考方案1】:

我感谢先前答案的努力,但发现缺乏基准。这是一个更好的微基准,请注意,它仍然是一个微基准。总是更喜欢测量真正的性能瓶颈,而不是过早地进行性能优化。

基准测试用于从 localStorage 读取和写入单个值、一百个对象的列表以及一万个对象的列表。

TL;DR:

single read: 0.0004ms, write: 0.0114ms
small list read: 0.0325ms, write: 0.0498ms
large list read: 3.1787ms, write: 3.3190ms

在 3.1 GHz 四核 Intel Core i7 上运行。铬 79。

read local storage - single x 2,439,715 ops/sec ±0.91% (62 runs sampled)
read local storage - small x 30,742 ops/sec ±0.78% (62 runs sampled)
read local storage - large x 315 ops/sec ±1.30% (60 runs sampled)
write local storage - single x 88,032 ops/sec ±4.25% (33 runs sampled)
write local storage - small x 20,079 ops/sec ±1.89% (59 runs sampled)
write local storage - large x 301 ops/sec ±1.09% (60 runs sampled)

Test: read local storage - single
mean: 0.0004098839352502669ms
margin of error: ±0.000003731514453196282ms
devation: ±0.00001499080315635531ms

Test: read local storage - small
mean: 0.03252840093744983ms
margin of error: ±0.0002551322114660716ms
devation: ±0.001024955633672395ms

Test: read local storage - large
mean: 3.1786666666666674ms
margin of error: ±0.041479799689699ms
devation: ±0.16392915653288143ms

Test: write local storage - single
mean: 0.011359496605398242ms
margin of error: ±0.00048286808926899016ms
devation: ±0.0014152377493978731ms

Test: write local storage - small
mean: 0.04980309857651518ms
margin of error: ±0.0009408982120607311ms
devation: ±0.0036873348473201325ms

Test: write local storage - large
mean: 3.31899154589372ms
margin of error: ±0.03605551145015122ms
devation: ±0.14249224018921072ms

如果你愿意,这里有一个 sn-p 可以自己运行。

<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/benchmark/2.1.4/benchmark.min.js"></script>
<script>
  const suite = new Benchmark.Suite();

  const genNum = () => Math.floor(Math.random() * 1000000);

  const genObj = () => (
    target: String(genNum()),
    swap: String(genNum()),
    price: genNum()
  );

  const printStats = test =>
    console.log(
      `Test: $test.name
mean: $test.stats.mean * 1000ms
margin of error: ±$test.stats.moe * 1000ms
devation: ±$test.stats.deviation * 1000ms`
    );

  const singleNum = String(genNum());
  const smallList = _.range(100).map(genObj);
  const largeList = _.range(10000).map(genObj);

  const singleKey = "single-key";
  const smallKey = "small-key";
  const largeKey = "large-key";

  localStorage.setItem(singleKey, singleNum);
  localStorage.setItem(smallKey, JSON.stringify(smallList));
  localStorage.setItem(largeKey, JSON.stringify(largeList));

  suite
    .add("read local storage - single", function() 
      const readData = localStorage.getItem(singleKey);
    )
    .add("read local storage - small", function() 
      const readData = JSON.parse(localStorage.getItem(smallKey));
    )
    .add("read local storage - large", function() 
      const readData = JSON.parse(localStorage.getItem(largeKey));
    )
    .add("write local storage - single", function() 
      localStorage.setItem("single_write_key", singleNum);
    )
    .add("write local storage - small", function() 
      localStorage.setItem("small_write_key", JSON.stringify(smallList));
    )
    .add("write local storage - large", function() 
      localStorage.setItem("large_write_key", JSON.stringify(largeList));
    )
    .on("cycle", function(event) 
      console.log(String(event.target));
    )
    .on("complete", function() 
      this.forEach(printStats);
    )
    .run( async: true );
</script>

【讨论】:

【参考方案2】:

苹果对苹果

localStorage 与对象存储进行比较没有太大价值,两者在 JavaScript 中的用途不同。您可能只需要在您的应用程序中触摸几次localStorage,其余工作将在内存中完成。

本地存储与 Cookie

localStorage 更好的比较是它的经典对应物document.cookielocalStoragedocument.cookie 的主要目的是在浏览器刷新期间保持一个值。

我在codsandbox.io上整理了一个例子

localStoragedocument.cookie 快两个数量级。 Object 存储比 localStorage 快一个数量级(无关,但添加以供参考)。

localStorage 是迄今为止在浏览器刷新期间保持值最快的机制。

请注意,我已经预编译了 cookie 正则表达式 getter,以便尽可能快地生成 cookie,并使用浏览器性能 API 进行准确测量。所有测试都执行一组唯一键,然后获取相同的键。

【讨论】:

这不是重点,也没有回答问题。也许我想决定是否应该将共享变量的缓存保留在常规存储中(并定期更新),还是将其保留在 localStorage 中并经常检查?这就是问题的重点,而这并没有回答它。 定义“常规存储”。从用例的角度来看,将值粘贴到全局变量中与 localStorage 没有太多共同之处。从localStorage 读取数据会将其从磁盘读取到内存中,因此您将AA + B 进行比较。一种是在内存中临时使用,另一种是持久化到磁盘。您不会在同一个过道中购买 RAM 和硬盘驱动器。这个问题对localStorage 造成了不好的影响,甚至没有将其与经典的cookies 进行比较。 如果你想办法存储对象并尝试使用本地存储来做到这一点,我敢打赌大部分时间将用于将对象移动到字符串并返回。例如最流行的方法是 JSON stringify,这是一个有点慢的操作。 @LukasLiesis 确实,JSON stringify 非常常用来序列化为字符串以插入本地存储,但这不应该包含在本地存储本身的基准测试中。我不喜欢这个问题的许多答案是他们将 localStorage 访问与常规变量访问进行比较,而两者几乎没有共同之处。为了回答这个问题,localStorage 似乎没有缓存读取,但称其为“非常慢”是用词不当。它比它的持久替代品(cookies)快得多。【参考方案3】:

对于它的价值,这是一个jsperf test。

在 FF7 和 IE9 中,localStorage 的基准使用明显比访问常规对象属性。当然,这只是一个微基准,不一定反映实际使用情况或性能...

从我的 FF 7 运行中提取的示例以显示“显着变慢”的含义,以操作/秒为单位:

本机本地存储笔记 小套装 374,397 13,657 10 个不同的项目 大集合 2,256 68 100 个不同的项目 读取偏差 10,266 342 1 次写入,10 次读取,10 个不同的项目

还有restrictions on what can be put in localStorage。 YMMV。

【讨论】:

但是常规对象不会被持久化。这就像将变量访问与 SQL 存储进行比较。它们有不同的目的,前者总是比后者快得多。 @cchamberlain 知道仍然很重要,例如在考虑是否缓存页面内经常使用的数据(并且可能需要管理跨多个选项卡/窗口的一致性)或始终从localStorage 获取它时。我目前正面临 OAuth 刷新令牌和其他持久应用程序状态的决定。巧合的是,另一位 Lukas 已经在您的回答下方讨论了这个问题。【参考方案4】:

刚刚做了一个小基准测试。我计划做的是经常从 localStorage 获取一些数据,我想知道它会阻塞。虽然 localStorage.getItem 是同步操作,但听起来可能很吓人,但真的吗?

第一次测试调用 100 万次 localStorage 以获取确实存在的项目。 第二次测试调用 100 万次 localStorage 以获取不存在的项目。

结果:

“找到的项目:每次调用 0.0007991071428571318 毫秒”

“未找到项目:每次调用 0.0006365004639793477ms”

简而言之 - 只需使用它。它不需要时间。 1 毫秒的 0.0007。

https://jsbin.com/resuziqefa/edit?js,console

 let results = [];
 let sum = 0;
 localStorage.setItem('foo', 'foo bar baz foo bar baz foo bar baz foo');

 for (let i = 0; i < 1000000; i++) 
   let a = performance.now();
   localStorage.getItem('foo');
   let result = performance.now() - a;
   sum += result;
   results.push(result);
 

 console.log(`Item found: $sum / results.lengthms per call`);

 results = [];
 sum = 0;
 for (let i = 0; i < 1000000; i++) 
   let a = performance.now();
   localStorage.getItem('bar');
   let result = performance.now() - a;
   sum += result;
   results.push(result);
 

 console.log(`Item not found: $sum / results.lengthms per call`);

【讨论】:

您需要一个参考点,它与变量访问相比如何? @Kugel 这样做不是为了比较,而是为了检查 localhost 存储是否运行得足够快,对整体性能没有影响,或者至少没有影响。我想知道应该如何编写测试来对你想要比较的东西进行真正的比较,这肯定超出了这个问题的范围。当动作发生在 0.0007ms 范围内时,即使这个测试也可能不够准确,并且没有涵盖 CPU 的工作原理和 JS 引擎本身。我的兴趣是检查 localhost 通常是否“快”。 你用大对象而不是简单的字符串测试过这个吗? @Sergiu 1. Localstorage 仅存储字符串,不存储对象。 2. 根据浏览器的不同,可以存储多少数据是有限制的,这些限制并不大,比如 5Mb。我不知道对你来说什么是“大”。 其实我问的是当值包含一个大字符串而不是几个字符时是否有任何性能差异?

以上是关于localStorage 的速度/成本的主要内容,如果未能解决你的问题,请参考以下文章

window.localStorage 与 chrome.storage.local

如何手动清除localStorage中的数据

HTML5的local storage存储的数据到底存到哪去了

怎么去检测某一个网页下localStorage的剩余容量

[Html5]sessionStorage和localStorage常见操作

HTML5WebSQLDatabase与localStorage用途?