Bug Tracker

Opened 5 years ago

Closed 5 years ago

Last modified 4 years ago

#14839 closed bug (migrated)

jQuery.data first access performance very bad

Reported by: daliuskal@… Owned by: Rick Waldron
Priority: high Milestone: 1.12/2.2
Component: data Version: 2.1.0
Keywords: Cc:
Blocked by: Blocking:

Description

http://jsfiddle.net/6DLSw/12/ 2s+ first time, ~50ms later on. The first run is extremely slow.

http://jsfiddle.net/6DLSw/13/ ~50ms when jQuery.data(el) was already called (note that the key doesn't matter).

The bug seems to have started with version 2.0.2 or so, when the jQuery.data was rewritten.

Using 1.11.0 on first case takes ~190ms first time, other times it's ~60ms.

This is affecting jquery ui performance very badly, e.g. http://jsfiddle.net/6DLSw/6/ - first red rectangle drop takes 2s+.

Change History (20)

comment:1 Changed 5 years ago by Rick Waldron

Owner: set to Rick Waldron
Status: newassigned

The internal design of jQuery.data and jQuery.fn.data will be changing again in a forth coming release.

comment:2 Changed 5 years ago by dmethvin

Component: unfileddata
Milestone: None1.12/2.2
Priority: undecidedhigh

comment:3 Changed 5 years ago by dmethvin

#15059 is a duplicate of this ticket.

comment:4 Changed 5 years ago by jbedard

Sorry for the dupe.

Is the internal design change the solution proposed for #11570? Or are there more plans? The pull request for #11570 is still using Object.defineProperties, so I assume the data creation will still be very slow...

comment:5 Changed 5 years ago by dmethvin

Yes, the PR is mainly a proof of concept to see if we could attach data directly to DOM elements to avoid doing cleanup and reduce the chances of leaks. I suspect we'll need to abandon the nice encapsulation to get speed back.

comment:6 Changed 5 years ago by Rick Waldron

I'd like to see benchmarks for patterns that reflect real pathological use cases—setting data on 60k divs at once isn't realistic.

Not using Object.defineProperty means:

  • the expando property is visible on all objects with data, for elements this isn't really a big deal, but extant code wants to use var o = {}; $(o).data(...) which means that o will now have an writable, configurable and enumerable property for _both_ user data and jQuery internal data.
  • data properties appearing in for-in, the array returned by Object.keys and jQuery.each iterations
  • data properties appearing in JSON, which can be fixed by falling back to the old toJSON hack
  • ...but then you have 1 too many properties on every object, which fails tests. Of course this too can be patched, but then the object that exists as the value of this expando won't match what's returned by .data()
Version 0, edited 5 years ago by Rick Waldron (next)

comment:7 Changed 5 years ago by Rick Waldron

data properties appearing in JSON, which can be fixed by falling back to the old toJSON hack

This also means breaking:

  • jQuery.hasData agrees no data exists even when an empty data obj exists
  • data() returns entire data object with expected properties
  • Retrieve data object from a wrapped JS object (#7524)
  • removeData does not clear the object
  • Make sure that the right number of properties came through.
  • Data appears as expected.
  • Data is empty.
  • Sanity Check
  • The data didn't change even though the data-* attrs did

I'm confident all of these can be "fixed" but I'm concerned that the resulting code will be a maintenance nightmare.

comment:8 Changed 5 years ago by jbedard

I'm not sure if the defineProperty/ies is the only or main cause of this, I thought it was (http://jsperf.com/v1-v2-data), but after a quick test replacing it with a plain assignment my use case was still significantly slower then jQuery1 (but this was 1 quick test...). If the defineProp is a big factor could a simple assignment be used for elements and still use defineProp for other objects?

My use case is a table where every cell has data. With approx 10k cells (500 rows, 20 columns) jQuery2 loads about 66% slower (~2.5s vs ~1.5s). That extra 1s is mainly Data.key (~700ms) and GC (~300ms).

Here's an example (50 rows x 10 columns) that seems to reproduce it: http://jsperf.com/jq2-data/5

From profiling the 2 cases it looks like this jsperf reproduces almost the exact same issue I'm having. With jQuery1 the forced relayout is most expensive (~25%), the clone/append/data/GC are all fairly small (2-5% each). With jQuery2 Data.key (~30%) and GC (~25%) skyrocket making the relayout only ~10%.

It looks like this might only really effect chrome though? That surprised me...

comment:9 Changed 5 years ago by jbedard

It looks like Chrome is acting up due to the long data expando property name. I assume the longer name is treated differently or causes the element properties to be stored differently?

This seems to fix everything: https://github.com/jbedard/jquery/commit/ed3a9887d16b098a7c784cc4920185d9e5c53e2a

That change makes http://jsperf.com/jq2-data/5 run 3x faster in chrome. It makes http://jsfiddle.net/6DLSw/12/ go from 3000+ to ~500 on the first run. It removes all the GC CPU usage. And it made a test I had locally (basically http://jsperf.com/jq2-data/5 called 100x) go from a 470m heap to 27m.

Here's an example showing the setting of different sized property names on elements: http://jsperf.com/long-name-on-dom In this example FF/IE actually improve more then Chrome so I don't understand why the original bug seems to mainly effect Chrome and not the others. Maybe only Chrome also has the memory issues?

Also note that using plain assignment instead of defineProperty/ies (maybe only for elements?) still makes it another 5x faster. Only then does Data.key get reduced enough that it uses less time then the DOM manipulations (like v1) in the jsperf and jsfiddle examples...

comment:10 Changed 5 years ago by dmethvin

Awesome, @jbedard, thanks for drilling down on this! It seems like we should be able to use just a uid there, the critical thing is that we allow multiple jQueries to work on an element without conflict and jQuery.expando should ensure that.

@rwaldron, what do you think?

comment:11 Changed 5 years ago by jbedard

https://code.google.com/p/chromium/issues/detail?id=378607 might be the cause of this, meaning it would be the '.' from Math.random() causing the issue not the length of the string.

comment:12 Changed 5 years ago by dmethvin

That's interesting. At least the fix would be easy.

comment:13 Changed 5 years ago by jbedard

It looks like Sizzle also puts a property onto DOM elements containing a dash ("sizzle-timestamp")...

Any update on this? It would be great to at least remove the "-"s for now to avoid the main chrome issue...

comment:14 Changed 5 years ago by jbedard

The "delete elem[ internalKey ]" in $.cleanData seems to cause the same thing. When combined with https://code.google.com/p/v8/issues/detail?id=2073#c26 this can lead to chrome crashing. In my case I'm deleting a giant grid with >300k nodes, and the delete line cause each node to increase in memory over 10x.

comment:15 Changed 5 years ago by markelog

After landing https://github.com/jquery/jquery/pull/1662 it seems we improved here, but

Also note that using plain assignment instead of defineProperty/ies (maybe only for elements?) still makes it another 5x faster

Sounds like using defineProperties is bad idea, as of now that is, should we get back to using plain assignment?

comment:16 Changed 5 years ago by jbedard

Even switching from defineProperties to defineProperty (since we only have one) helps a bit. But really I think something such as https://github.com/jbedard/jquery/commit/d44885b3603dd41ebc47ac889bc55a9a0da0fd4f would be best.

comment:17 in reply to:  16 Changed 5 years ago by gibson042

Replying to jbedard:

Even switching from defineProperties to defineProperty (since we only have one) helps a bit. But really I think something such as https://github.com/jbedard/jquery/commit/d44885b3603dd41ebc47ac889bc55a9a0da0fd4f would be best.

I agree; our current implementation is just trying to be a bit too clever.

comment:18 Changed 5 years ago by m_gol

Resolution: migrated
Status: assignedclosed

comment:19 Changed 4 years ago by Jason Bedard

Data: avoid non-alphanumeric chars in expando properties

Ref chromium issue 378607 Ref #14839 Closes gh-1662

Changeset: b876e50834f109619b9607b1d63c3869e2f04ffe

comment:20 Changed 4 years ago by Jason Bedard

Data: avoid non-alphanumeric chars in expando properties

(cherry-picked from 0cdec797de23555c95a70978f4d9e06f3b041330)

Ref chromium issue 378607 Ref #14839 Closes gh-1662

Changeset: 9448be717ce7ccc21dccf7e3d8d929252f270a89

Note: See TracTickets for help on using tickets.