Why is storage.session so slow?

1,174 views
Skip to first unread message

Jackie Han

unread,
Dec 11, 2022, 2:55:53 AM12/11/22
to Chromium Extensions
In my test, chrome.storage.session is as slow as chrome.storage.local !

// test code
const t0 = performance.now();
let obj = await chrome.storage.session.get(null);
const t1 = performance.now();
console.log(`sesion get ${t1 - t0} milliseconds.`);


It usually spends 10-70 ms on my computer, the same time as storage.local (ssd drive).

Jackie Han

wOxxOm

unread,
Dec 11, 2022, 3:57:00 AM12/11/22
to Chromium Extensions, Jackie Han
It's slow because the data is sent to the browser process (a different physical process), which means everything is serialized to be passed via IPC and de-serialized afterwards.

wOxxOm

unread,
Dec 11, 2022, 3:59:37 AM12/11/22
to Chromium Extensions, wOxxOm, Jackie Han
Apparently, the IPC in Chrome is just that slow.

Jackie Han

unread,
Dec 11, 2022, 4:24:36 AM12/11/22
to wOxxOm, Chromium Extensions
serialization and deserialization should not be too slow since the data is very small or no data.
IPC may take some time.
`session.setAccessLevel` may take some time (browser calls it internally).
Maybe there are some other reasons I don't know.

If storage.session is 10 times faster than storage.local, I will use storage.session as the cache. But if they are almost as fast, then it is unnecessary to use it, which will increase complexity in code.

Juraj M.

unread,
Dec 11, 2022, 5:12:30 AM12/11/22
to Chromium Extensions, Jackie Han, Chromium Extensions, wOxxOm
For me it prints:
sesion get 0.19999999925494194 milliseconds.
When executing from extension page and store has only one number value inside.

wOxxOm

unread,
Dec 11, 2022, 5:12:46 AM12/11/22
to Chromium Extensions, Jackie Han, Chromium Extensions, wOxxOm
Try IndexedDB, it's the fastest asynchronous storage.

Jackie Han

unread,
Dec 11, 2022, 5:44:01 AM12/11/22
to Juraj M., Chromium Extensions, wOxxOm
If I test it 100 times continually.

for (var i = 0; i < 100; i++) {
  await test();
}

It is usually 0.x ms - 1.x ms(or a few ms), but the first a few times are usually slow (8-80ms, usually > 10 ms). I usually get settings only one time when the page starts.

wOxxOm

unread,
Dec 11, 2022, 6:11:15 AM12/11/22
to Chromium Extensions, Jackie Han, Chromium Extensions, wOxxOm, juraj....@gmail.com
>  usually get settings only one time when the page starts.

Ah, it's the standard asynchronous behavior then:

1. the API is called but its result will be returned only in the future in a separate JS event loop task, never in the current one;
2. the remainder of your script runs in the current JS event loop task;
3. in the next JS event loop task (or even later depending on the speed of IPC) the result of API call is returned.

The delay you observe is the 2nd step and it may take even several seconds if the script is that huge.

Jackie Han

unread,
Dec 11, 2022, 7:03:35 AM12/11/22
to wOxxOm, Chromium Extensions, juraj....@gmail.com
This is not related to other parts of code. You can remove all other code, only left await chrome.storage.session.get(null); .

wOxxOm

unread,
Dec 11, 2022, 7:14:44 AM12/11/22
to Chromium Extensions, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm
In that case I can't reproduce the problem. It's ~1ms here.

Jackie Han

unread,
Dec 11, 2022, 7:17:20 AM12/11/22
to wOxxOm, Chromium Extensions, juraj....@gmail.com
Screenshot 2022-12-11 at 20.15.08.png

Above is a typical result on my computer.

All the code is below, no other code.
async function test() {

  // test code
  const t0 = performance.now();
  let obj = await chrome.storage.session.get(null);
  const t1 = performance.now();
  console.log(`sesion get ${t1 - t0} milliseconds.`);
}

Jackie Han

unread,
Dec 11, 2022, 7:27:38 AM12/11/22
to wOxxOm, Chromium Extensions, juraj....@gmail.com
In my previous message > "usually get settings only one time when the page starts."
I mean the speed is slow only at the first few times, but I only call it one time(not many times), so it is slow in my use case.

wOxxOm

unread,
Dec 11, 2022, 7:36:22 AM12/11/22
to Chromium Extensions, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm
Yes, and it's ~1ms for me even during the startup of the browser.

Jackie Han

unread,
Dec 11, 2022, 8:14:17 AM12/11/22
to wOxxOm, Chromium Extensions, juraj....@gmail.com
Besides JavaScript, rendering, painting and compiling code also run in the main thread when a page starts.

So I put the test code in the service worker, this avoids the effects of rendering and painting.
But in my test, it is still slow for the first few times(~10ms or > 10ms, occasionally < 10ms).  After the first few times, it is ~1ms.

wOxxOm

unread,
Dec 11, 2022, 8:18:12 AM12/11/22
to Chromium Extensions, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm
In that case it's still the standard behavior of asynchronous code only with the added time for DOM/CSS/rendering in the 2nd step, in other words it's not specific to `chrome` API.

Jackie Han

unread,
Dec 11, 2022, 8:26:02 AM12/11/22
to wOxxOm, Chromium Extensions, juraj....@gmail.com
it's not specific to `chrome` API

Execute time is for session.get(), no other code.

wOxxOm

unread,
Dec 11, 2022, 8:47:26 AM12/11/22
to Chromium Extensions, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm
Yes, any asynchronous code that delivers results in a separate JS event loop task behaves like this.

Jackie Han

unread,
Dec 11, 2022, 8:59:04 AM12/11/22
to wOxxOm, Chromium Extensions, juraj....@gmail.com
If run local.get() (not session) 100 times, it is also ~1ms except the first few times.

In conclusion, 
storage.local reads data from disk, so developers don't expect it is very fast.
storage.session reads data from memory, developers expect it is very fast.
But at present, storage.session is not fast as expected, sometimes the speed of storage.local and storage.session is the same.

Robbi

unread,
Dec 11, 2022, 9:16:42 AM12/11/22
to Chromium Extensions, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm
If i'm not mistaken you're using a Mac.
Here is the real problem. Throw it out the window  🤣

Jackie Han

unread,
Dec 11, 2022, 9:31:47 AM12/11/22
to Robbi, Chromium Extensions, juraj....@gmail.com, wOxxOm
Last month, I bought a mini PC (Windows) as my test machine. Both mac and windows are low-end Intel CPUs.
I just tested on Windows, the speed of storage.local and storage.session is the same.

wOxxOm

unread,
Dec 11, 2022, 9:32:06 AM12/11/22
to Chromium Extensions, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm
In a visible DOM-based page the first run will be always slow because it includes the time to do one-time jobs like building initial DOM/CSS layout. The first run of chrome.storage.session is 1ms here in a service worker as it doesn't have DOM, the same should apply to an MV2 background page because its DOM is invisible/inert. Anyway, this is how any asynchronous API works that delivers results in a separate task. It's doesn't mean the API itself is slow.

As for chrome.storage.local, it's slow even with small amounts of data not just because it reads from disk (reading may be cached by OS, probably), but because it serializes values multiple times e.g. get() does it three times: the browser process deserializes from LevelDB, IPC serializes the data, IPC sends it, IPC deserializes it. In set() there's an additional call for JSON.stringify to calculate the size of data.

In the end, with small amounts of data the speed is not decided by the nature of the backend storage of the data but by the overhead and rendering. Theoretically it's possible to write a much faster storage.session API that would directly access some shared memory block via low-level system calls without the need for IPC, but it's not trivial to implement. Still, might be something to suggest on https://crbug.com.

Jackie Han

unread,
Dec 11, 2022, 9:49:34 AM12/11/22
to wOxxOm, Chromium Extensions, juraj....@gmail.com
I agree. I just want to remind that `storage.session` cannot be used as a means of performance optimization at the moment. It is only suitable for temporary data that does not need to be saved.
Message has been deleted
Message has been deleted

Robbi

unread,
Dec 14, 2022, 10:12:28 PM12/14/22
to Chromium Extensions, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm
> Try IndexedDB, it's the fastest asynchronous storage.

I have to admit that I have always pushed indexedDB both in this channel and on SO.
Intrigued by this thread, I had fun building a performance comparison tool (extension) between indexedDB and storage.local.
Let's put aside storage.session for now which, being an object in memory, will hopefully be faster than all of its alternatives.

To those who want to take up the challenge and install this extension, I ask first of all, since we are talking about performance, to verify the goodness of my code and to correct it where they think it could be beneficial.

Secondly, I ask you to do some tests and then to discuss the results.

In the meantime, I'll tell you the results I got.
IndexedDB is good for read operations (both single and bulk), but it sucks for write operations (especially those referring to a single record).
Storage.local as far as I could verify (at least with my parameters) always wins.
It'd seem that my previous beliefs about indexedDB have partly gone down the drain.
However, indexedDB does not have the space limits imposed by storage.local and therefore it can be considered the tank that there is no bad weather or road that can stop it.
A strange thing that I have noticed in storage.local is that the cleaning of this storage is strangely slow when it is already empty.
I won't add anything else; I await your comments.


wOxxOm

unread,
Dec 15, 2022, 3:28:37 AM12/15/22
to Chromium Extensions, Robbi, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm
Multiple writes are slow in IndexedDB, indeed, this is a known weakness compared to the deprecated WebSQL. Overall your test shows that with simple objects there's no pronounced benefit, but when you store complex deeply nested objects with lots of properties, IndexedDB is faster. Also, it can directly store Blob/ArrayBuffer/Uint***Array, which in chrome.storage would be super slow due to the need to convert them to JS arrays/strings first. As always with microbenchmarking you need to find the area of its applicability in real life i.e. investigate what is usually stored in db and group the results, then imitate/approximate these groups in your tests.

Also, maybe IndexedDB waits for the physical write to complete before invoking the callback whereas chrome.storage returns right after dumping the data to LevelDB, which then throttles the writes. Try changing `durability` in db.transaction.

Juraj M.

unread,
Dec 15, 2022, 3:51:24 AM12/15/22
to Chromium Extensions, wOxxOm, Robbi, Jackie Han, Chromium Extensions, Juraj M.
But IndexedDB is crazy complicated to use and "uniquely buggy" in each browser!
I see people using "localStorage" API because it's sync and easy and using "storage.local" is too complicated for them with those "complex promises" :D. And you want them to use IndexedDB?

I do like IndexedDB, especially for the ability to store Blobs, but the amount of issues I had to deal with over the years and the terrible API makes it really hard to recommend for anything else than Blobs.
BTW, now I remember, I actually had to stop using it for Blobs in Safari because they were being corrupted somehow after each restart so I had to stick with base64 strings. And a bonus bug - if you store big blobs as base64 strings in Chromium, your browser may crash on start after allocating gigabytes of RAM :D, unless someone fixed that already. And a bonus tip - old version of CCleaner and Malwarebytes used to simply delete or corrupt IndexedDB in Firefox. Oh, so many memories :).

wOxxOm

unread,
Dec 15, 2022, 4:05:42 AM12/15/22
to Chromium Extensions, juraj....@gmail.com, wOxxOm, Robbi, Jackie Han, Chromium Extensions
> But IndexedDB is crazy complicated

There are libraries that offer Promise interface for IndexedDB and simplified API surface.
I often use my own micro-wrapper for put/get/delete with just one object store that is just ~20 lines of code and usage is almost as trivial as window.localStorage.


> and "uniquely buggy" in each browser!

There were bugs indeed, I've seen complaints for multiple extensions that had their DB wiped in the past, but personally I haven't encountered any of that in ~10 years I've been using chrome extensions in a Windows PC.

> but the amount of issues I had to deal with over the years and the terrible API makes it really hard to recommend for anything else than Blobs

IndexedDB is several times faster for deeply nested JS objects, so I'll keep using it and recommending it.

Robbi

unread,
Dec 15, 2022, 6:25:05 AM12/15/22
to Chromium Extensions, wOxxOm, juraj....@gmail.com, Robbi, Jackie Han, Chromium Extensions
Hi folk and  thanks for your all comments I consider more than relevant
I'm still "pretty" convinced  that using IDB instead of storage.local (or storage.session) for everyday work is a bit "venturesome".
My test is certainly primitive and stores only numbers.
Now I try to change `durability` in db.transaction and save an object with one\two nested objects and with other data types such as strings and booleans.
I could get very different results than the previous ones.

As for the library that I used (written by me, but writtable by many) I think this is essential to allow the normal operations of reading, writing and cleaning using the more "human" promises.
I don't think such changes will introduce much lag\delay.
Giving up the "masking" of IDB events with promises would certainly be a strong deterrent in using such an storage system in contexts of medium\large complexity.

The next thing I'll try with this "tool" is to delegate the undertracked operations to the SW.
Given the accidents I stumbled upon not too long ago (see thread), I'm afraid IDB will have more surprises in store for me.

I make some changes and then I'll report back to you.

Jackie Han

unread,
Dec 15, 2022, 6:53:18 AM12/15/22
to Robbi, Chromium Extensions, juraj....@gmail.com, wOxxOm
However, indexedDB does not have the space limits imposed by storage.local and therefore it can be considered the tank that there is no bad weather or road that can stop it.

indexedDB and other storage also have quota limits. Each browser is different. Chrome's limit see https://web.dev/storage-for-the-web/#how-much

There is a "unlimitedStorage" permission for storage.local to ignore the 5M limit. I am not sure whether it is also for other storages like indexedDB.

Robbi

unread,
Dec 15, 2022, 9:55:38 AM12/15/22
to Chromium Extensions, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm, Robbi
@Jackie Han, your observation is definitely relevant too . A while ago I did a search and found the link you suggested.
I understood that the browser can evict the data of a source without any warning when the latter exceed a certain limit unless a particular permission is requested (and granted). If I remember correctly this permission is requested with "navigator.storage.persisted()".
In the world of extensions, the danger of this unannounced emasculation would not seem concrete (I await confirmations or denials).
I also checked that invoking navigator.storage.persisted() from inside an extension does not give any grant.
However, we can run into an error when you exceed the quota limit.

I made some changes as @wOxxOm suggested. Now you can choose whether to operate in "strict" or "relaxed" mode.
Now I no longer save numbers but an object with other nested objects inside (up to the second level).
What to say? IDB performance improves and NOT A LITTLE!
Now IDB is playing on equal terms with storage.local even if, by a small margin, the latter still wins (at least in my opinion).
It remains, at least for me, to clarify what is meant by "relaxed".
Mozila says: "Using "relaxed" provides better performance, but with fewer guarantees. Web applications are encouraged to use "relaxed" for ephemeral data such as caches or quickly changing records, and "strict" in cases where reducing the risk of data loss outweighs the impact to performance and power."
Mozilla doesn't go into detail about "when" the data will be persistently saved to disk.
I mean, let's say I fill a  100Mb IDB, wait for the transaction to be committed (either with the dedicated event or with a promise) and then I close the browser after 5\10 seconds.
What should I expect when the browser restarts? The updated db, the one before this heavy transaction or even worse the damaged db?

Next step: "quite massive" transactions on IDB from inside the SW.

The previously created link has been invalidated.
The new link with the updated extension can be found at this Google Drive link

Robbi

unread,
Dec 15, 2022, 7:31:29 PM12/15/22
to Chromium Extensions, Robbi, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm
Mozilla also says: "In Firefox 40+ the complete event is fired after the OS has been told to write the data but potentially before that data has actually been flushed to disk. The complete event may thus be delivered quicker than before, however, there exists a small chance that the entire transaction will be lost if the OS crashes or there is a loss of system power before the data is flushed to disk. Since such catastrophic events are rare most consumers should not need to concern themselves further."
Ok, but what if the browser is closed after few sec since "complete event" ?

Other tests done with the same tool reveal that the more objects I put in storage.local (> 8k-15k items key-value), the slower it becomes in both reading and writing, while IDB gains ground.

The new link with the updated and corrected extension can be found at the same Google Drive link

wOxxOm

unread,
Dec 16, 2022, 4:56:57 AM12/16/22
to Chromium Extensions, Robbi, Jackie Han, Chromium Extensions, juraj....@gmail.com, wOxxOm
Ok, but what if the browser is closed after few sec since "complete event" ?

Should be ok. The quote about flushing refers to the OS, so "OS has been told to write the data" should mean that the data is already outside the browser.

Robbi

unread,
Dec 17, 2022, 5:53:39 AM12/17/22
to Chromium Extensions, wOxxOm, Robbi, Jackie Han, Chromium Extensions, juraj....@gmail.com
Great, good to know!
In certain contexts I should have set this property.
Thank you
Reply all
Reply to author
Forward
0 new messages