Closed Bug 1287277 Opened 10 years ago Closed 9 years ago

Awesomebar autocomplete perf optimizations

Categories

(Toolkit :: Places, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla50
Tracking Status
firefox49 --- fixed
firefox50 --- fixed

People

(Reporter: simon.lindholm10, Assigned: simon.lindholm10)

References

Details

Attachments

(6 files, 5 obsolete files)

7.38 KB, patch
Details | Diff | Splinter Review
1.83 KB, patch
simon.lindholm10
: review+
Details | Diff | Splinter Review
4.00 KB, patch
mak
: review+
Details | Diff | Splinter Review
5.34 KB, patch
simon.lindholm10
: review+
Details | Diff | Splinter Review
1.45 KB, patch
simon.lindholm10
: review+
Details | Diff | Splinter Review
1.45 KB, patch
mak
: review+
Details | Diff | Splinter Review
In the hopes of alleviating bug 1283329 a bit, while also being generally useful, I did some profiling of awesomebar autocomplete and tried to optimize it. On the test case of searching for a random three-letter string, I managed to improve the SQL parts of the performance by ~30% on my machine (i5-4200U, Linux, clang, -O2), mostly by removing unnecessary memory allocations. Perf profiles before/after: http://bit.ly/29TJuNQ http://bit.ly/2a28y7I I'm entirely new to this code, so let me know I'm doing something dumb, optimizing unnecessarily, setting the wrong reviewer, or whatever.
Attachment #8771675 - Flags: review?(mak77)
Attachment #8771676 - Flags: review?(mak77)
Attachment #8771677 - Flags: review?(mak77)
(hey, I did see memcmp show up in profiles a tiny bit, this might have been it)
Attachment #8771678 - Flags: review?(mak77)
I measured this to be a ~10% improvement.
Attachment #8771679 - Flags: review?(mak77)
Assignee: nobody → simon.lindholm10
Status: NEW → ASSIGNED
Hi, thank you for looking into these improvements, I'm glad you did it, cause very often we lack the time to go micro-optimizing all the stuff. I may take a little bit to review all of the changes since I have a couple higher priority fixes and requests, so please be patient.
Comment on attachment 8771675 [details] [diff] [review] 0001-Avoid-repeated-string-reallocations-in-NS_UnescapeUR.patch Review of attachment 8771675 [details] [diff] [review]: ----------------------------------------------------------------- I cannot review code in this component, so forwarding ::: xpcom/io/nsEscape.cpp @@ +579,5 @@ > NS_NOTREACHED("null pointer"); > return false; > } > > + NS_ASSERTION(aResult.IsEmpty(), In new code I'd probably suggest to use MOZ_ASSERT, that is a fatal assertion.
Attachment #8771675 - Flags: review?(nfroyd)
Attachment #8771675 - Flags: review?(mak77)
Attachment #8771675 - Flags: feedback+
Attachment #8771678 - Flags: review?(mak77) → review+
Comment on attachment 8771675 [details] [diff] [review] 0001-Avoid-repeated-string-reallocations-in-NS_UnescapeUR.patch Review of attachment 8771675 [details] [diff] [review]: ----------------------------------------------------------------- Looks good to me! Please modify the patch to include Mak's suggestion of using MOZ_ASSERT. Glancing through the callers, I don't think a fatal assertion will cause problems here.
Attachment #8771675 - Flags: review?(nfroyd) → review+
Comment on attachment 8771679 [details] [diff] [review] 0005-Avoid-querying-bookmark-title-and-tags-for-non-bookm.patch Review of attachment 8771679 [details] [diff] [review]: ----------------------------------------------------------------- I need some more details about this change, since I'm not sure where the saving generates, and maybe once figured the main cost, we could look into an approach that doesn't execute twice the group_concat. ::: toolkit/components/places/UnifiedComplete.js @@ +115,5 @@ > > // TODO bug 412736: in case of a frecency tie, we might break it with h.typed > // and h.visit_count. That is slower though, so not doing it yet... > +// NB: as a slight performance optimization, we only evaluate the "btitle" > +// and "tags" queries for bookmarked entries. I'd like to better understand where we are saving, cause the slowness here is: 1. the tags concact 2. looking through the tags in SQLFunctions We don't seem to save anything on the concat, since SQL_BOOKMARK_TAGS_FRAGMENT} doesn't have a CASE WHEN, and ideally non bookmarked entries atm should not have tags. so in those cases we should pass to AUTOCOMPLETE_MATCH an empty string. instead this seems to execute twice the group_concat, that doesn't look like a perf improvement to me. @@ +126,5 @@ > LEFT JOIN moz_openpages_temp t ON t.url = h.url > WHERE h.frecency <> 0 > AND AUTOCOMPLETE_MATCH(:searchString, h.url, > + CASE WHEN bookmarked THEN > + IFNULL(${SQL_BTITLE_FRAGMENT}, h.title) don't we already have btitle here, since the query includes SQL_BOOKMARK_TAGS_FRAGMENT? @@ +129,5 @@ > + CASE WHEN bookmarked THEN > + IFNULL(${SQL_BTITLE_FRAGMENT}, h.title) > + ELSE h.title END, > + CASE WHEN bookmarked THEN > + ${SQL_TAGS_FRAGMENT} and ditto for tags
Attachment #8771679 - Flags: review?(mak77)
The idea is to do lazy evaluation: when performing |SELECT expensive_function(...) WHERE ...|, the function only gets evaluated when the WHERE condition is fulfilled. > don't we already have btitle here, since the query includes SQL_BOOKMARK_TAGS_FRAGMENT? We do, but if I refer to it, sqlite will eagerly evaluate it (even when bookmarked is false). I'll recheck that what I'm saying is correct and rewrite the comment when I get time.
Comment on attachment 8771677 [details] [diff] [review] 0003-Avoid-unnecessary-string-copies-in-MatchAutoComplete.patch Review of attachment 8771677 [details] [diff] [review]: ----------------------------------------------------------------- I like the idea, I suggest to use the (start, end) constructors though, for coherence, what do you think? ::: toolkit/components/places/SQLFunctions.cpp @@ +221,5 @@ > if (aMatchBehavior == mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED) > + return fixedSpec; > + > + if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("http://"))) { > + fixedSpec.Rebind(fixedSpec, 7); To rebind on the same string, there is a constructor taking (start, end) that would probably be a better choice (ideally one could optimize the functions differently when binding to the same string vs to a different string, even if that doesn't currently happen). The end value can easily be buffered before the first if and reused. @@ +231,3 @@ > > + if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("www."))) { > + fixedSpec.Rebind(fixedSpec, 4); It may be more readable as return Substring(fixedSpec, 4); if Substring can be used on nsDependentStrings, I don't recall.
Attachment #8771677 - Flags: review?(mak77) → feedback+
(In reply to Simon Lindholm from comment #10) > The idea is to do lazy evaluation: when performing |SELECT > expensive_function(...) WHERE ...|, the function only gets evaluated when > the WHERE condition is fulfilled. > > > don't we already have btitle here, since the query includes SQL_BOOKMARK_TAGS_FRAGMENT? > > We do, but if I refer to it, sqlite will eagerly evaluate it (even when > bookmarked is false). I see. On the other side though, for bookmarks, we will evaluate it twice :(
> To rebind on the same string, there is a constructor taking (start, end) > that would probably be a better choice (ideally one could optimize the > functions differently when binding to the same string vs to a different > string, even if that doesn't currently happen). > The end value can easily be buffered before the first if and reused. Hm. I couldn't find a way to use the constructor (or Substring), since nsDependentSubstring's assignment operator is deleted... > if Substring can be used on nsDependentStrings, I don't recall. Should be possible. I like the symmetry though. > I see. On the other side though, for bookmarks, we will evaluate it twice :( Only if actually SELECTed, which happens for at most 10 items. Non-SELECTed bookmarks will take the additional perf hit of two CASEs, but that shouldn't be very expensive I believe.
Comment on attachment 8771676 [details] [diff] [review] 0002-Add-mozIStorageValueArray.AsDependentUTF8String-and-.patch Review of attachment 8771676 [details] [diff] [review]: ----------------------------------------------------------------- ::: storage/mozIStorageValueArray.idl @@ +5,5 @@ > > #include "nsISupports.idl" > %{C++ > #include "mozilla/DebugOnly.h" > +#include "nsStringGlue.h" I'd prefer not to add further string dependencies here, can we do the conversion from AsSharedUTF8String in SQLFunctions.cpp instead?
Attachment #8771676 - Flags: review?(mak77) → feedback+
(In reply to Simon Lindholm from comment #13) > > To rebind on the same string, there is a constructor taking (start, end) > > that would probably be a better choice (ideally one could optimize the > > functions differently when binding to the same string vs to a different > > string, even if that doesn't currently happen). > > The end value can easily be buffered before the first if and reused. > > Hm. I couldn't find a way to use the constructor (or Substring), since > nsDependentSubstring's assignment operator is deleted... Sorry I meant Rebind(start, end); instead of Rebind(string, start);
Comment on attachment 8771679 [details] [diff] [review] 0005-Avoid-querying-bookmark-title-and-tags-for-non-bookm.patch OK, I need to look into this again and do some testing.
Attachment #8771679 - Flags: feedback?(mak77)
> I'd prefer not to add further string dependencies here, can we do the > conversion from AsSharedUTF8String in SQLFunctions.cpp instead? Sure. > Sorry I meant Rebind(start, end); instead of Rebind(string, start); Can do, but note that it would look like |fixedSpec.Rebind(fixedSpec.BeginReading() + 7, end);| unless I add another Rebind overload taking integer indices. > OK, I need to look into this again and do some testing. Great!
(In reply to Simon Lindholm from comment #17) > Can do, but note that it would look like > |fixedSpec.Rebind(fixedSpec.BeginReading() + 7, end);| unless I add another > Rebind overload taking integer indices. Sigh, I thought it was taking indices in one overload. Nevermind, let's keep it as-is.
Attachment #8771677 - Flags: feedback+ → review+
Attachment #8771676 - Flags: feedback+ → review-
I had some problems measuring meaningful improvements from the SQL change. But I see where it may be faster. Though, I wonder if doing the CASE WHEN but using btitle and tags inside it, will still evaluate them for discarded rows. so basically: AND AUTOCOMPLETE_MATCH(:searchString, h.url, CASE WHEN bookmarked THEN IFNULL(btitle}, h.title) ELSE h.title END, CASE WHEN bookmarked THEN tags ELSE '' END, I couldn't measure a difference between this and the one where the expressions are repeated twice. It would be unfortunate if sqlite would evaluate btitle and tags if the case condition is not satisfied.
Flags: needinfo?(simon.lindholm10)
Ah, you appear to be entirely correct! It's a bit surprising to me to see sqlite perform lazy evaluation on all variables, but I suppose it makes some amount of sense. I'm seeing the following timings ± stdevs: A: 361896 us ± 11440 (154 data points) B: 333050 us ± 12844 (150 data points) C: 335006 us ± 17755 (166 data points) where A is the original code, B uses your last comment's version, and C is my patch's. I don't have error bars on that, but with 150 data points it should be fairly safe to say that either B or C is a ~8% win over A. I'll revise the patch to avoid the duplication.
Flags: needinfo?(simon.lindholm10)
Attachment #8771679 - Flags: feedback?(mak77)
Attachment #8771675 - Attachment is obsolete: true
Attachment #8775238 - Flags: review+
Attachment #8771676 - Attachment is obsolete: true
Attachment #8775240 - Flags: review?(mak77)
Attachment #8771677 - Attachment is obsolete: true
Attachment #8775241 - Flags: review+
Attachment #8771678 - Attachment is obsolete: true
Attachment #8775242 - Flags: review+
Attachment #8771679 - Attachment is obsolete: true
Attachment #8775243 - Flags: review?(mak77)
Attachment #8775243 - Flags: review?(mak77) → review+
Comment on attachment 8775240 [details] [diff] [review] 0002-Bug-1287277-Use-AsSharedUTF8String-in-MatchAutoCompl.patch Review of attachment 8775240 [details] [diff] [review]: ----------------------------------------------------------------- ::: toolkit/components/places/SQLFunctions.cpp @@ +181,5 @@ > + getSharedString(mozIStorageValueArray* aValues, uint32_t aIndex) { > + uint32_t len; > + const char* str = aValues->AsSharedUTF8String(aIndex, &len); > + if (!str) { > + return nsDependentCString("", (uint32_t)0); I was just a little bit worried about dangling pointers, but Sqlite manages the lifetime of most of them and that satisfies our needs. But for this innocuous looking empty string... who owns it? Maybe we should create outside an EmptyCString() and pass it to this method, so we can build a dependentString whose lifetime is certain?
Attachment #8775240 - Flags: review?(mak77)
Flags: needinfo?(simon.lindholm10)
(In reply to Marco Bonardo [::mak] (Away 6-20 Aug) from comment #28) > ::: toolkit/components/places/SQLFunctions.cpp > @@ +181,5 @@ > > + getSharedString(mozIStorageValueArray* aValues, uint32_t aIndex) { > > + uint32_t len; > > + const char* str = aValues->AsSharedUTF8String(aIndex, &len); > > + if (!str) { > > + return nsDependentCString("", (uint32_t)0); > > I was just a little bit worried about dangling pointers, but Sqlite manages > the lifetime of most of them and that satisfies our needs. > But for this innocuous looking empty string... who owns it? It ought to reside in the static data section of the program binary, so it effectively lives forever.
Comment on attachment 8775240 [details] [diff] [review] 0002-Bug-1287277-Use-AsSharedUTF8String-in-MatchAutoCompl.patch Review of attachment 8775240 [details] [diff] [review]: ----------------------------------------------------------------- You are right. Considered its size, it's likely a price we can pay.
Attachment #8775240 - Flags: review+
Right. In practice it might have been fine also to return an nsDependentCSubstring(nullptr, 0), but that felt a lot iffier. Setting checkin-needed.
Flags: needinfo?(simon.lindholm10)
Keywords: checkin-needed
Attachment #8774700 - Attachment description: Benchmarking code, for reference → [DO NOT LAND] Benchmarking code, for reference
Pushed by ryanvm@gmail.com: https://hg.mozilla.org/integration/mozilla-inbound/rev/fd2b1e87c27e Avoid repeated string reallocations in NS_UnescapeURL. r=nfroyd https://hg.mozilla.org/integration/mozilla-inbound/rev/bcfdd291e2f1 Use AsSharedUTF8String in MatchAutoCompleteFunction. r=mak https://hg.mozilla.org/integration/mozilla-inbound/rev/dcb3c6e5c58b Avoid unnecessary string copies in MatchAutoCompleteFunction::fixupURISpec. r=mak https://hg.mozilla.org/integration/mozilla-inbound/rev/ca925530409a Silly micro-optimization. r=mak https://hg.mozilla.org/integration/mozilla-inbound/rev/a83df31aa5bd Avoid querying bookmark title and tags for non-bookmarked items. r=mak
Keywords: checkin-needed
Blocks: 1283329
let's wait some nightlies, then I think we should evaluate uplifting these to 49 early in the cycle, any perf optimization in the location bar is worth it.
Flags: needinfo?(mak77)
Comment on attachment 8775243 [details] [diff] [review] 0005-Bug-1287277-Avoid-querying-bookmark-title-and-tags-f.patch To simplify things I'm setting the request only on one patch, but all should land: Approval Request Comment [Feature/regressing bug #]: none, this is a performance improvement. We just released a redesign in 48, and some users are suffering perf degrade in certain cases, this may help with that. [User impact if declined]: awesomebar is slower for some users [Describe test coverage new/current, TreeHerder]: nightly/aurora [Risks and why]: the changes are small optimizations that shouldnot have a scary impact, it's also safe to backout in case of problems. [String/UUID change made/needed]: none
Flags: needinfo?(mak77)
Attachment #8775243 - Flags: approval-mozilla-beta?
Comment on attachment 8775243 [details] [diff] [review] 0005-Bug-1287277-Avoid-querying-bookmark-title-and-tags-f.patch Review of attachment 8775243 [details] [diff] [review]: ----------------------------------------------------------------- This patch improves the awesomebar performance. Take it in 49 beta. Should be in 49 beta 2.
Attachment #8775243 - Flags: approval-mozilla-beta? → approval-mozilla-beta+
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: