Closed Bug 837141 Opened 12 years ago Closed 5 months ago

horrible performance storing indexeddb blobs

Categories

(Core :: Storage: IndexedDB, defect, P5)

23 Branch
x86_64
Windows 7
defect

Tracking

()

RESOLVED INCOMPLETE

People

(Reporter: ygutfreund, Unassigned)

References

()

Details

(Whiteboard: dom-lws-bugdash-triage)

Attachments

(2 files, 1 obsolete file)

User Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0 Build ID: 20130116073211 Steps to reproduce: This is a FYI, I am not demanding a fix (even if I could :-) I have been doing some performance testing of indexedDB blobs. Backround: I a creating an offline map, that using PNG image tiles for a SLIPPY map. Actual results: FireFox is getting much worse performance than IE or Chrome Results Offline blob cache for PNG slippy maps Testing 171 PNG files (total of 3.2MB) Platforms tested: Chrome v24, FireFox 18, IE 10 Should also work with Chrome & FF for Android Fetch from web server using XHR2 (supported on almost all browsers) for blob download from web server I went with XHR2-Lib by Phil Parsons, which is very much like JQUERY .ajax() https://github.com/p-m-p/xhr2-lib Storage IndexedDB for IE and FireFox Chrome: Polyfill (blob stored using FileSystem API, reference kept in IndexedDB) polyfill A Must read article on "How the browsers store IndexedDB data" http://www.aaron-powell.com/web/indexeddb-storage Note: FireFox uses SQLlite for the NOSQL IndexedDB. That might be the reason for the slow performance. (blobs stored separately) Note: Microsoft IE uses the extensible storage engine: http://en.wikipedia.org/wiki/Extensible_Storage_Engine Note: Chrome uses LevelDB http://code.google.com/p/leveldb/ Display I am using Leaflet http://leafletjs.com/ to show the map tiles I used the functional tile layer plugin by Ishmael Smyrnow for fetching the tile layer from the DB https://github.com/ismyrnow/Leaflet.functionaltilelayer I compared the DB-based tiles layer with a purely local (localhost://) storage There is no noticeable difference in performance! between using IndexedDB and local files! Results Chrome: Fetch (6.551s), Store (8.247s), Total Elapsed Time: (13.714s) FireFox: Fetch (0.422s), Store (31.519s), Total Elapsed Time: (32.836s) IE 10: Fetch (0.668s), Store: (0.896s), Total Elapsed Time: (3.758s) Expected results: I would expect at least as good performance as Chrome, since both are storing the files outside of the DB in a filestore, and then placing a reference in the DB to the blob path. But I am only providing this as a FYI for your aid. I am not looking for resolution.
Component: Untriaged → DOM: IndexedDB
Product: Firefox → Core
My first guess is that those files are not stored outside of SQLite database in Firefox . Could you please send a code snippet how you store those blobs ?
Attached file Test case (obsolete) —
I created a simple test, it creates 171 blobs (each 19623 bytes) and stores them in one transaction. Actual storing is instant on my computer (around 125 ms)
Attached file Test case
Ok, I also added storing of array buffers (stored in SQLite database) to the test and storing of buffers and blobs in separate transactions. Storing of buffers in one transaction: 99ms Storing of blobs in one transaction: 108ms Storing of buffers in separate transactions: 721ms Storing of blobs in separate transactions: 896ms Note, I have an SSD disk Slowness could also relate to handling of queued transactions, bug 776800 But 172 transactions seem not so many.
Attachment #709271 - Attachment is obsolete: true
I ran those tests on Aurora 20.0a2 I'll try it with nigthly and Kyle's fix for bug 776800.
(In reply to Jan Varga [:janv] from comment #4) > I ran those tests on Aurora 20.0a2 > > I'll try it with nigthly and Kyle's fix for bug 776800. Storing of buffers in one transaction: 112ms Storing of blobs in one transaction: 122ms Storing of buffers in separate transactions: 667ms Storing of blobs in separate transactions: 824ms So Kyle's patch doesn't helper here, numbers are similar and the difference is probably just a noise
I apologize for not following up on this. I was on travel with just an iPhone. I am using identical code on FF, IE10, and Chrome. (the only hack is that Chrome does not handle a IndexedDB PUT of a binary blob (in my case a PNG). So I catch that and store it to the Chrome FileSystemAPI. But then FF does pretty much the same. It creates an entry in the SQLLite DB and then stores the blob as a file. (not visible to me). All my PNGs are first fetched with an XHR2 request, and on the async completetion callback, I create a new transaction (for each PNG) and then do the put. In the code below, you will see that I first fetch a manifest (JSON file) which has the list of all the PNG files. It then does the XHR2 for each PNG file, and then does an transaction with a single PUT. NOTE: the server is LOCALHOST. Which is a IIS 7.5 webserver running on the same Win7 box as the browser running Firefox. What I am trying to understand is why FF is so much slower than IE10 or Chrome. // executed when all js and css is loaded (i.e. document ready event) var baseURL = "http://localhost/Poland/"; var fileList = null; var blobList = []; var loadTime = null; var storeTime = null; var ImageStore = null; var ChromeFlag = false; var DBVersion = 1; var DBNAME = "BlobDB"; var DB = null; var ImageStoreCnt = 0; var showID = "12-2274-1388"; window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; function startDB() { if (!window.indexedDB) { window.alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available."); } testXHR(); setup(); getManifest(); } function testXHR() { if ($xhr.supported()) { $("#support").text("XHR2 Supported"); } else { $("#support").text("XHR2 Missing"); } } function setup() { $("#deleteDB").click(deleteDB); $("#openDB").click(openDB); $("#refillDB").click(clearStores); ChromeFlag = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; $("#FSFlag").text(ChromeFlag.toString()); $xhr.defaultError(function (text, code) { console.log("xhr error: " + code + ": ", text); console.log("xhr url: ", this.xhr2data.url); }); } function getManifest() { $xhr.ajax({ url: baseURL + "manifest.json", dataType: "json", success: parseManifest }); } function parseManifest(data) { fileList = data.files; $("#status").text("manifest loaded"); } function openDB() { var request = indexedDB.open(DBNAME, DBVersion); request.onerror = function (event) { console.log("Error opening database"); }; request.onsuccess = function (event) { console.log("Success opening database"); DB = event.target.result; DB.onerror = function (e) { console.log("DB error: " + e.target.errorCode); }; DB.onversionchange = function (e) { e.target.close(); }; }; request.onupgradeneeded = function (event) { console.log("VersionUpgrade: Adding Object Stores to Database"); DB = event.target.result; DB.createObjectStore("Images"); }; if (ChromeFlag) openFS(); } function deleteDB() { if (DB != null) { DB.close(); } if (ChromeFlag) deleteImageDir(); var request = indexedDB.deleteDatabase(DBNAME); request.onerror = function (event) { console.log("Error deleting database"); }; request.onsuccess = function (event) { console.log("Success deleting database"); }; } function dbErrors(event) { console.log("DB error: " + event.target.errorCode); } function clearStores() { console.log("Clearing and Refilling Object Stores"); var trans, store; trans = DB.transaction(["Images"], "readwrite"); store = trans.objectStore("Images"); store.clear(); trans.oncomplete = saveManifest; } function saveManifest() { var trans, store, req; loadTime = new Date(); trans = DB.transaction(["Images"], "readwrite"); store = trans.objectStore("Images"); req = store.put(fileList, "Manifest"); req.onsucess = getBlobs(); } function getBlobs() { console.log("starting to fetch blobs"); $.each(fileList, function (i, val) { var path = baseURL + val.path; $xhr.ajax({ url: path, dataType: "blob", success: function (data) { saveBlob(data, val.size, val.id); } }); }); } function saveBlob(blob, length, id) { if (blob.size != length) { console.log("Blob Length found: " + blob.size + " expected: " + length); } if (blobList.length == 0) { storeTime = new Date(); } blobList.push(id); storeBlob(blob, id); if (blobList.length == fileList.length) { loadingComplete(); } } function storeBlob(blob, id) { console.log("storing blob: " + id); var trans, store, req; trans = DB.transaction(["Images"], "readwrite"); store = trans.objectStore("Images"); var record = { id: id, blobFile: false }; if (ChromeFlag) { record.blobFile = true; record.path = id; fileBlob(blob, id); } else { record.blob = blob; } req = store.put(record, id); req.onsuccess = function (event) { console.log("stored blob: " + id); ImageStoreCnt++; if (ImageStoreCnt == fileList.length) { storeComplete(); } }; } function loadingComplete() { var elapsed = new Date() - loadTime; $("#totalTime").text((elapsed / 1000).toString() + " Get"); } function storeComplete() { console.log("Done Storing Tiles"); var elapsed = new Date() - loadTime; $("#totalTime").text((elapsed / 1000).toString()); }
One other point. I have no doubt your test case works. But I think what I am trying to do is more realisitic. Which is to sync the data from a cloud "DB" to the local web based DB. I can also tell you that the XHR2 requests finish just as fast on FireFox as they do on Chrome and IE10. (in fact FF is faster at .44s). What this points to is the overlapping IDB transactions. But who would just generate artificial data in the browser and store it. More likely it comes from the web? I am just trying to give input and feedback here. I am not making a political stand. I am hoping this is useful for your development.
Sure, that's all ok. Could you take a look at https://developer.mozilla.org/en-US/docs/Performance/Profiling_with_the_Built-in_Profiler Would you be able to do the profiling ?
Attached file First Profile log
Here is one profile log
I grabbed the nightly build as suggested, and then ran the profiler. I have a second log that is about 4.2MB which is too large to attach, but if you give me an email address I will send that also. (we have no way to post things for public urls).
jan dot varga at gmail dot com
sent.
Version: 18 Branch → 21 Branch
I have been doing some more work on this. I have created demo in CodePen (similar to jsFiddle, but nicer): http://codepen.io/DrYSG/pen/hpqoD Here is the write-up on what this demo does, and how to use it. Try it out on the latest FF builds. https://groups.google.com/forum/#!topic/pouchdb/RG6wUsAi2R0 You can see that even the latest version of FireFox have very poor performance for blob storage in indexedDB. This latest demo is using the PouchDB library to create a friendly API for indexedDB. But I don't see PouchDB as the issue. (see earlier comments on this issue).
Summary: performance of indexeddb blobs → horrible performance storing indexeddb blobs
I updated the title, to be more specific. It is specifically the save blob to IDB that is performing very poor compared to IE10 and Chrome. The Fetch is not that bad. If you go to http://gis.stackexchange.com/questions/62070/offline-slippy-map-tiles-database-for-leaflet/62073#62073 I describe in detail the system and the tests. You will see that after I store the blobs, I then display these blobs on a map. That seems to have fine performance (the fetch from IDB). I can also tell you that the XHR2 fetches for FireFox are slower than IE10 or Chrome, which you can also work at.
I don't think Chrome has support for storing files in IDB. AFAIK they are still working on it and the current implementation only stores a reference to the file or something like that. So you can't use Chrome for performance comparison. Hm, maybe IE10 doesn't wait for files/blobs to be fully copied before the transaction is finished.
Chromium developers are actively working on Binary Blob support in IDB: http://code.google.com/p/chromium/issues/detail?id=108012 . But right now my demo uses PouchDB, which transforms to Base64, and then stores it in IDB (not a reference). So the size and performance for Chrome should be worse and binary blobs. It is not. My timing and debug for IE10 shows that it is fully XHR2 and saving the blobs. (a different demo than the one mentioned above). IE10 has superb XHR2 and IDB performance (much better than Chrome), and I am working with the PouchDB folks to get it fully supported in PouchDB. See my prior tests with IE10 and raw IDB: http://stackoverflow.com/questions/14113278/storing-image-data-for-offline-web-application-client-side-storage-database (last answer in the list).
Hm, could you change your code to use array buffers, so it won't be stored in Firefox as separate standalone files ? I'm curious if it will make any difference.
It is a possibility. I would prefer this to be done either at the FF or PouchDB level. So that sort of experimentation is not my highest priority. Right now, I am thinking of deprecating FF support and focusing on Chrome.
Actually Jan, there is something slightly disturbing in what I reading here. But perhaps I am reading this entirely wrong. Is what you are saying that JavaScript developers should be aware of the internals of how FF stores binary blobs? Should they be writing javascript code that is customized for Chrome, Opera, IE10, and webkit? Or should they expect that since W3C says that binary blobs in IDB is part of the spec, that the browser should "do the right thing" and store the binary blob either as separate stand-alone files or as internal array buffers as a implementation decision. Should these dependencies be written into the JavaScript code, what would happen when browsers change? Or is the idea just to fork the CodePen and try something out? Which anyone can do?
Jan is asking you to test that and see if it makes any difference. That information may help us narrow down the bug. Nobody is expecting web developers to familiarize themselves with the implementation details of our blob storage code.
What Kyle said. I'm curious if the perf problem is mostly related to how we store blobs or if it is something else.
So, I did misunderstand. My apoligizes. I will be on biz travel next week, so if anyone wants to FORK the CodePen and try that, feel free.
Version: 21 Branch → 23 Branch
I was able to hack PouchDB so that it will save the blobs as Base64 rather than as binary blobs. Performance is not any better. So it is not about how you store blobs alone. A re-write of PouchDB to use ArrayBuffers is beyond the scope of my funding. At this point I am pretty well ready to declare FireFox broken for our customers, and since IE11 is vastly imporoved, I think that both it and Chrome will be sufficient. Bye Bye FF Blob Version: http://codepen.io/DrYSG/pen/kdzft Base64 version:
Oh yes, if you get xhr errors when the loop hits after about 40 good xhr fetch, IDB save interations, the source of the problem is most likely the fact that IE11 and Chrome both support SPDY (Google Drive) but Chrome does not. I had to force all the xhr requests to run serially on FF. Another reason for my current displeasure with FF, in addition to the lack of a true non-sql IDB. I wish you folks well, but I for this app, I am telling our customers to use Chrome or IE.
(In reply to ygutfreund from comment #23) > At this point I am pretty well ready to declare FireFox broken for our > customers, and since IE11 is vastly imporoved, I think that both it and > Chrome will be sufficient. > > Bye Bye FF Sorry, childish statements like this is not helping you get attention to this bug. I understand if you feel the need to drop Firefox support, but that can certainly be done without unhelpful comments like the one above. That said, it'd be great to have an actual testcase as an attachment here. The code in comment 6 looks great, but isn't a full testcase which can be run directly. Also, you really should wait for the "commit" event on the transaction rather than the "success" event for each request. The data isn't guaranteed to have been written to disk when the request finishes, but it will have been written to disk when the "commit" event is fired. Though it's quite likely that this is *not* the problem and that there is a real Firefox issue here.
The profile (attachment 712950 [details]) seems not a correct one, how about the one sent offline at comment 12?
Priority: -- → P5
Severity: normal → S3
Status: UNCONFIRMED → RESOLVED
Closed: 5 months ago
Resolution: --- → INCOMPLETE
Whiteboard: dom-lws-bugdash-triage
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: