Skip to main content

Bug Tracker

Side navigation

#14839 closed bug (migrated)

Opened March 02, 2014 03:34AM UTC

Closed October 20, 2014 11:20PM UTC

Last modified December 11, 2014 10:57PM UTC

jQuery.data first access performance very bad

Reported by: daliuskal@gmail.com Owned by: rwaldron
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+.

Attachments (0)
Change History (20)

Changed March 02, 2014 07:14PM UTC by rwaldron comment:1

owner: → rwaldron
status: newassigned

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

Changed March 03, 2014 03:50PM UTC by dmethvin comment:2

component: unfileddata
milestone: None1.12/2.2
priority: undecidedhigh

Changed April 28, 2014 01:59PM UTC by dmethvin comment:3

#15059 is a duplicate of this ticket.

Changed April 28, 2014 04:12PM UTC by jbedard comment:4

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...

Changed April 28, 2014 04:24PM UTC by dmethvin comment:5

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.

Changed May 07, 2014 09:20PM UTC by rwaldron comment:6

_comment0: 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() \ \ 1399500309388516

I'd like to see benchmarks for patterns that reflect real pathological use cases—accessing jQuery 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()

Changed May 07, 2014 09:48PM UTC by rwaldron comment:7

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.

Changed May 13, 2014 07:41AM UTC by jbedard comment:8

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...

Changed May 15, 2014 08:57AM UTC by jbedard comment:9

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...

Changed May 15, 2014 12:13PM UTC by dmethvin comment:10

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?

Changed June 05, 2014 05:08AM UTC by jbedard comment:11

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.

Changed June 05, 2014 05:12AM UTC by dmethvin comment:12

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

Changed September 10, 2014 08:29PM UTC by jbedard comment:13

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...

Changed September 10, 2014 09:41PM UTC by jbedard comment:14

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.

Changed September 26, 2014 11:11AM UTC by markelog comment:15

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?

Changed September 26, 2014 04:05PM UTC by jbedard comment:16

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.

Changed September 26, 2014 08:43PM UTC by gibson042 comment:17

Replying to [comment:16 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.

Changed October 20, 2014 11:20PM UTC by m_gol comment:18

resolution: → migrated
status: assignedclosed

Changed December 11, 2014 10:50PM UTC by Jason Bedard comment:19

Data: avoid non-alphanumeric chars in expando properties

Ref chromium issue 378607

Ref #14839

Closes gh-1662

Changeset: b876e50834f109619b9607b1d63c3869e2f04ffe

Changed December 11, 2014 10:57PM UTC by Jason Bedard comment:20

Data: avoid non-alphanumeric chars in expando properties

(cherry-picked from 0cdec797de23555c95a70978f4d9e06f3b041330)

Ref chromium issue 378607

Ref #14839

Closes gh-1662

Changeset: 9448be717ce7ccc21dccf7e3d8d929252f270a89