Closed Bug 299689 Opened 19 years ago Closed 19 years ago

Getting lots of elements by ID can be slow

Categories

(Core :: DOM: Core & HTML, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED

People

(Reporter: bzbarsky, Assigned: bzbarsky)

References

(Blocks 1 open bug)

Details

(Keywords: perf)

Attachments

(1 file, 2 obsolete files)

The testcase is at the URL of bug 297959, the getElementById test.  That test
gets each of the table cells by id, which makes the whole thing O(N^2) in number
of cells, since we have to keep walking over parts of the DOM that we've walked
over before.

Ian's suggestion was that after we've had some number of misses of the
getElementById hashtable we should just assume more misses will come, walk over
the whole DOM and prefill the hashtable.  In fact, I've been thinking we should
switch to a mode in which the hashtable is maintained live altogether.
Attached patch Proposed patch (obsolete) — Splinter Review
This makes us switch to a live ID table once we've had 128 misses for
GetElementById in this document.

That said, perhaps it would make sense to eliminate some of this complexity and
always have a live table?
Attachment #188265 - Flags: superreview?(jst)
Attachment #188265 - Flags: review?(jst)
Keywords: perf
Blocks: 218308
OS: Linux → All
Hardware: PC → All
Attachment #188265 - Flags: superreview?(jst)
Attachment #188265 - Flags: review?(jst)
Attached patch Updated to tip (obsolete) — Splinter Review
Attachment #188265 - Attachment is obsolete: true
Attachment #194001 - Flags: superreview?(jst)
Attachment #194001 - Flags: review?(jst)
Blocks: 203448
Comment on attachment 194001 [details] [diff] [review]
Updated to tip

+  PRBool IdTableIsLive() {
+    // live if we've had over 127 misses
+    return (mIdMissCount & 0x80) != 0;
+  }

Make that const, and isn't 127 sortof a high number here? How about 63?

- In nsHTMLDocument::UpdateIdTableEntry():

+  PRBool liveTable = IdTableIsLive();
+  PLDHashOperator op = liveTable ? PL_DHASH_ADD : PL_DHASH_LOOKUP;
   IdAndNameMapEntry *entry =
     NS_STATIC_CAST(IdAndNameMapEntry *,
		    PL_DHashTableOperate(&mIdAndNameHashTable, &aId,
-					 PL_DHASH_LOOKUP));
+					 op));

-  if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
+  if ((liveTable && entry) || PL_DHASH_ENTRY_IS_BUSY(entry)) 

PL_DHashTableOperate() can return a null entry for PL_DHASH_ADD if we're OOM,
you need to not do the PL_DHASH_ENTRY_IS_BUSY(entry) check if entry is null.

r+sr=jst
Attachment #194001 - Flags: superreview?(jst)
Attachment #194001 - Flags: superreview+
Attachment #194001 - Flags: review?(jst)
Attachment #194001 - Flags: review+
Assignee: general → bzbarsky
Attachment #194001 - Attachment is obsolete: true
Status: NEW → ASSIGNED
Fix checked in to trunk.
Status: ASSIGNED → RESOLVED
Closed: 19 years ago
Resolution: --- → FIXED
Comment on attachment 195157 [details] [diff] [review]
Updated to comments

>+  PRBool liveTable = IdTableIsLive();
>+  PLDHashOperator op = liveTable ? PL_DHASH_ADD : PL_DHASH_LOOKUP;
>   IdAndNameMapEntry *entry =
>     NS_STATIC_CAST(IdAndNameMapEntry *,
>                    PL_DHashTableOperate(&mIdAndNameHashTable, &aId,
>-                                        PL_DHASH_LOOKUP));
>+                                        op));
> 
>-  if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
>+  if (entry && (liveTable || PL_DHASH_ENTRY_IS_BUSY(entry))) {
>     entry->mIdContent = aContent;
>   }

If !entry, you should bail with OOM.

Do you really want to probe the table without adding if !liveTable?  If so, you
could simplify the logic of the || expression to just
PL_DHASH_ENTRY_IS_BUSY(entry).	If not, you don't need that || expression at
all.

/be
(In reply to comment #6)
> If not, you don't need that || expression at all.

By which I mean, always PL_DHASH_ADD.  It's like a LOOKUP when the entry exists
(it doesn't disturb the entry).

/be
(In reply to comment #7)
> (In reply to comment #6)
> > If not, you don't need that || expression at all.
> 
> By which I mean, always PL_DHASH_ADD.  It's like a LOOKUP when the entry exists
> (it doesn't disturb the entry).

So bz pointed out the obvious, that we don't want to add unconditionally.  So my
"then" clause ("you could simplify the logic of the || expression to just
PL_DHASH_ENTRY_IS_BUSY(entry)") applies, because if ADD succeeds (does not fail
with OOM and return null), it returns a busy entry pointer.

/be
I just checked this in with r+sr=brendan:

-  if (entry && (liveTable || PL_DHASH_ENTRY_IS_BUSY(entry))) {
+  if (!entry) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  
+  if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
Fixed on trunk.
Depends on: 311681
Not exactly sure how to write a decent test for this...
Flags: in-testsuite?
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: