Conversation
|
Explain why not #576 |
|
Mainly because you were right in the discussion on #576: maintaining the boundary pointer is the actual clean fix. Bisection in #576 is a neat compromise to stay stateless, but it still does O(log N) iterations per insertion and each step has to loop past nulls/whitespace. By tracking It also avoids touching That said, if you'd rather avoid adding state to |
|
I don't care, you'll have to get a maintainer to agree one way or another I do think that if you point your bot at an issue that already has an open PR - then you should have an obligation to explain yourself.
sounds as though your worst case is worse |
|
Fair point, that's completely on me. I saw your comment on #576 about maintaining the pointer, wanted to see if it was actually doable without adding too much complexity, and got ahead of myself without linking #576 in the description. Definitely wasn't trying to step on David's toes or push redundant code. Happy to leave it to @davidpavlovschi and @frostming to decide which direction they prefer, or close this if they'd rather stick with #576. |
Fixes #540
Problem
As reported by @dimbleby in #540, inserting keys into a table becomes progressively slower (O(N^2) overall). When populating a table with 8,000 keys, it took ~16.2 seconds locally.
Profiling the benchmark showed that almost all the time (~95%) is spent in
_get_last_index_before_table(). On every key insertion, it iterated overself._bodyfrom index 0 all the way to the end, doingisinstancechecks on every item.Changes
Table presence flag & cached index:
_has_tablesand_first_table_idxinContainer._raw_append,_insert_at, and_insert_after, update these when aTableorAoTis added/shifted.remove,_remove_at, and_replace_at, invalidate_first_table_idxif the affected index was the first table.__copy__and__setstate__(for pickle).Reverse scan instead of full scan:
_has_tablesis False (the vast majority of tables and sub-tables without child table headers), the boundary is simplylen(self._body)._has_tablesis True, we use_first_table_idxdirectly (falling back to a full scan if invalid).Benchmark
Using the snippet from #540:
Before:
n=1000: 238.1 msn=2000: 973.9 msn=4000: 3968.4 msn=8000: 16247.3 msAfter:
n=1000: 7.3 ms (32x faster)n=2000: 14.8 ms (65x faster)n=4000: 30.6 ms (130x faster)n=8000: 63.2 ms (257x faster)n=16000: 126.8 ms (linear O(N), ~7.9 us/key)All existing tests pass and a regression test for insertion scaling/order has been added.