Skip to content

Consider using SortedList instead of Dictionary in HttpHeaders #62846

Description

@geoffkizer

The Dictionary we allocate in HttpHeaders for the headers table seems to use a significant amount of memory. Since the headers table itself is typically not that large, it seems reasonable to use SortedList instead and trade off reduced memory usage for a small amount of added CPU cost on update and retrieval -- I doubt this added CPU cost is significant, whereas the memory savings may be significant.

Activity

  1. added this to the Future milestone on Dec 15, 2021
  2. ghost added
    untriagedNew issue has not been triaged by the area owner
    on Dec 15, 2021
  3. MihaZupan commented on Dec 15, 2021

    @MihaZupan
    Member

    This may require a custom SortedList implementation (or improvements to the existing collection).
    Using the existing implementation as-is would likely be worse (for example it boxes the enumerator)

  4. geoffkizer commented on Dec 15, 2021

    @geoffkizer
    ContributorAuthor

    Well that's unfortunate. Aside from boxing the enumerator, are there other known issues?

  5. MihaZupan commented on Dec 15, 2021

    @MihaZupan
    Member

    An insertion has O(n) runtime, so I think that disqualifies this type of collection from a DOS security perspective as well.

  6. geoffkizer commented on Dec 15, 2021

    @geoffkizer
    ContributorAuthor

    An insertion has O(n) runtime, so I think that disqualifies this type of collection from a DOS security perspective as well.

    I'm not sure it matters. We limit the size of the response headers that we will accept, so there's a bound on the total number of headers we will add. While O(n^2) isn't great, it's not necessarily a DOS as long as n is smallish.

  7. geoffkizer commented on Dec 15, 2021

    @geoffkizer
    ContributorAuthor

    Another alternative here might be to simply have an unsorted list of key value pairs. This has cheap insertion but makes both lookup and removal expensive. That might not be a bad tradeoff though. And if we did it this way, we could actually preserve the original header ordering, which seems like a nice benefit.

    For stuff like YARP we would want to just enumerate the raw headers and not worry about grouping by header name, which means that "raw enumeration" for these sorts of scenarios would be cheap as well. Unfortunately we defined the NonValidated collection to group by header name as well...

  8. MihaZupan commented on Dec 15, 2021

    @MihaZupan
    Member

    While O(n^2) isn't great, it's not necessarily a DOS as long as n is smallish.

    Right, but as of right now n could still reach a few thousand by default which isn't ideal.

  9. Clockwork-Muse commented on Dec 15, 2021

    @Clockwork-Muse
    Contributor

    An insertion has O(n) runtime, so I think that disqualifies this type of collection from a DOS security perspective as well.

    .... do we insert headers singly, or as a range?

  10. danmoseley commented on Dec 15, 2021

    @danmoseley
    Contributor

    Using the existing implementation as-is would likely be worse (for example it boxes the enumerator)

    Are you referring to the non generic SortedList? SortedList<K,V> should not do this.

  11. MihaZupan commented on Dec 15, 2021

    @MihaZupan
    Member

    The generic as well. The enumerator is a struct, but the type is not publicly exposed so the GetEnumerator returns an IEnumerator.

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()

  12. added
    enhancementProduct code improvement that does NOT require public API changes/additions
    and removed
    untriagedNew issue has not been triaged by the area owner
    on Dec 16, 2021
  13. karelz commented on Dec 16, 2021

    @karelz
    Member

    Triage:

    • Would be nice to save some memory if we don't hurt CPU too much
    • It may help with order of headers - we had few issues in the space

    Further design discussion:

    • Idea: Could we have a list/array as backing field and create something like Dictionary for string lookups? (they are rare)
    • In extreme case we might want to add another API, which would return multiple headers (not happening today), unsorted
    • Or perhaps just add another IEnumerable<string, string>
  14. geoffkizer commented on Dec 16, 2021

    @geoffkizer
    ContributorAuthor

    Similarly, ConcurrentDictionary<K, V> boxes its enumerator: https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.getenumerator?view=net-6.0

    I noticed this in the same trace. It's a small impact in this case because we only enumerate the concurrent dictionary when scavenging connections.

    I'm not sure why these collections were implemented so that their enumerators must be boxed. Is this simply an oversight? Can we fix it? It's a breaking change, but a very small one...

  15. gfoidl commented on Dec 16, 2021

    @gfoidl
    Member

    For a dictionary approach maybe dotnet/aspnetcore#31360 is also of interest (the AdaptiveCapacityDictionary).

  16. stephentoub commented on Dec 17, 2021

    @stephentoub
    Member

    It's a breaking change, but a very small one...

    It's a binary breaking change. I don't think it's that small. Anyone enumerating one of these today would fail at run time.

    Similarly, ConcurrentDictionary<K, V> boxes its enumerator:

    #25448

  17. geoffkizer commented on Dec 17, 2021

    @geoffkizer
    ContributorAuthor

    It's a binary breaking change. I don't think it's that small. Anyone enumerating one of these today would fail at run time.

    Makes sense, thanks for clarifying.

  18. self-assigned this
    on Dec 18, 2021
  19. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Dec 18, 2021
  20. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Jan 20, 2022
  21. ghost locked as resolved and limited conversation to collaborators on Feb 19, 2022
  22. modified the milestones: Future, 7.0.0 on Apr 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area-System.Net.HttpenhancementProduct code improvement that does NOT require public API changes/additionstenet-performancePerformance related issue

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions