Skip to content

WiP http: add lazy map for eliminating O(n) header operations.#9261

Closed
jmarantz wants to merge 18 commits intoenvoyproxy:masterfrom
jmarantz:header-lazy-map
Closed

WiP http: add lazy map for eliminating O(n) header operations.#9261
jmarantz wants to merge 18 commits intoenvoyproxy:masterfrom
jmarantz:header-lazy-map

Conversation

@jmarantz
Copy link
Copy Markdown
Contributor

@jmarantz jmarantz commented Dec 7, 2019

For an explanation of how to fill out the fields, please see the relevant section
in PULL_REQUESTS.md

Description:
Risk Level:
Testing:
Docs Changes:
Release Notes:
[Optional Fixes #Issue]
[Optional Deprecated:]

Signed-off-by: Joshua Marantz <jmarantz@google.com>
…more clearly.

Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
@jmarantz jmarantz changed the title http: add lazy map for eliminating O(n) header operations. WiP http: add lazy map for eliminating O(n) header operations. Dec 7, 2019
@jmarantz
Copy link
Copy Markdown
Contributor Author

jmarantz commented Dec 7, 2019

Speed-test comparison with no maps, with a flat_hash_map, and with a multi_map. To me it looks like with a reasonable # of headers, multi_map is the best choice.

Screen Shot 2019-12-07 at 9 23 39 AM

Signed-off-by: Joshua Marantz <jmarantz@google.com>
…his adds complexity but makes common cases faster.

Signed-off-by: Joshua Marantz <jmarantz@google.com>
@jmarantz
Copy link
Copy Markdown
Contributor Author

jmarantz commented Dec 7, 2019

An easier to read graph covering just the benchmarks that have O(N) growth with no map:

Screen Shot 2019-12-07 at 1 32 00 PM

@mattklein123
Copy link
Copy Markdown
Member

@jmarantz do you mind summarizing what we are doing/testing here? Also, as an aside, did you try the absl btree maps? They should be faster than the std maps AFAIK.

@jmarantz
Copy link
Copy Markdown
Contributor Author

jmarantz commented Dec 7, 2019

I don't mind :) This is not really in shape to be a mergeable PR as it makes things strictly more complex. But I wanted to think quantitatively about the overhead have maintaining maps. FWIW we do see HeaderMap stuff show up in flame-graphs and it's not the lowest hanging fruit right now but it does come up. I suspect in our environment we have a bunch of non-O(1) headers, especially Set-Cookie which needs to stay O(N) as that's currently tied to coalescing.

I haven't tried -- and didn't know about -- absl::btree . But one more thing is that the multi-map variant in this draft-PR could also allow for a fast (O(log n) + O(# matches)) impl of removePrefix(), though I don't know specifically if that's coming up as an issue.

I thing my thoughts on plausible end-states include:

  • drop this PR; e.g. assume there's never enough non-O(1) headers in the real world for this to matter. I suspect 20+ response headers would be pretty common but the perf-tests don't yet cover that point.
  • accept the complexity of one of these options (we should try btree too) in exchange for removing the O(N) exposure, with much fuzzing to give us confidence.
  • solving header map: allow extensions to statically register O(1) headers #4815 by updating the trie on startup and dropping this PR; basically allowing any extension to easily add O(1) headers, per discussion on that issue.

@mattklein123
Copy link
Copy Markdown
Member

OK thanks makes sense to me. I think this is a worthwhile thing to investigate for sure.

struct HeaderEntryImpl;
using HeaderNode = std::list<HeaderEntryImpl>::iterator;
#if HEADER_MAP_USE_FLAT_HASH_MAP
using HeaderNodeVector = std::vector<HeaderNode>;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would try https://github.com/abseil/abseil-cpp/blob/master/absl/container/inlined_vector.h here with a default size of 1 which would be the common case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea; I'll give that a try.


HeaderMapImpl::HeaderLazyMap::iterator
HeaderMapImpl::HeaderList::find(absl::string_view key) const {
if (lazy_map_.empty()) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this also use maybeMakeMap() to be consistent and also share the construction code?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this could use a refactor; I'll clean that up.

@mattklein123
Copy link
Copy Markdown
Member

Cool stuff just left some random comments from a quick read, will look more later. :)

@mattklein123 mattklein123 self-assigned this Dec 7, 2019
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
@mattklein123
Copy link
Copy Markdown
Member

@jmarantz what are your thoughts on next steps for this PR?

/wait-any

@jmarantz
Copy link
Copy Markdown
Contributor Author

Will be moving forward with this when I get some coding time :)

I started experimenting with your suggestions and the inlined-vector improved things a bit, the btree didn't seem to help, but I would not say my experiments are conclusive.

@mattklein123
Copy link
Copy Markdown
Member

OK I will put this into waiting for now with no stalebot. Let us know when you feel ready to have a conversation about whether we want to go with this approach or not.

@mattklein123 mattklein123 added waiting no stalebot Disables stalebot from closing an issue labels Dec 10, 2019
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
…t break tests.

Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
…nings.

Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
@mattklein123 mattklein123 removed the no stalebot Disables stalebot from closing an issue label Jul 16, 2020
@stale
Copy link
Copy Markdown

stale bot commented Jul 25, 2020

This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot added the stale stalebot believes this issue/PR has not been touched recently label Jul 25, 2020
@stale
Copy link
Copy Markdown

stale bot commented Aug 1, 2020

This pull request has been automatically closed because it has not had activity in the last 14 days. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot closed this Aug 1, 2020
mattklein123 pushed a commit that referenced this pull request Sep 30, 2020
This is a continuation of #9261. Adding a map to headermap's HeaderList to access headers by key in O(1) instead of O(n).
In addition some benchmarks that emulate encoding and decoding of headers, and route matching by headers are added.

Signed-off-by: Adi Suissa-Peleg <adip@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale stalebot believes this issue/PR has not been touched recently waiting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants