Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Jiterpreter performance issues around Dictionary and GenericEqualityComparer #82948

@kg

Description

@kg

Description

The way Dictionary and GenericEqualityComparer interact causes performance issues in the Jiterpreter that would be hard to fix without changes to the interpreter and/or the BCL. For more details see dotnet/perf-autofiling-issues#12762 (comment) but to summarize:

The Dictionary<int, int>.ContainsValue benchmark regressed once the jiterpreter was enabled.

  • Dictionary.ContainsValue is written assuming multiple optimizations will happen, and very few (or none) of them happen in this configuration
  • GenericEqualityComparer<int> is used for the benchmark that regressed and it is also very inefficient in this configuration
  • The result is slow mono interpreter opcodes that also hit some of the jiterpreter's worst case scenarios
  • It is difficult (perhaps impossible) to fix this on the jiterpreter side, the regression can probably be made smaller but eliminating it would require blocking compilation for Dictionary and perhaps GenericEqualityComparer. I am unable to imagine a heuristic that would detect this reliably.

Configuration

This applies to WebAssembly non-AOT (mono interpreter) with the jiterpreter enabled.

Regression?

Performance regresses for this code with the jiterpreter enabled

Data

https://pvscmdupload.blob.core.windows.net/reports/allTestHistory/refs/heads/main_x64_ubuntu%2018.04_CompliationMode=wasm_RunKind=micro/System.Collections.Tests.Perf_Dictionary.ContainsValue(Items%3a%203000).html

Analysis

Candidate changes that might entirely make this go away, while also just making these code paths faster in the mono interpreter:

  • Turn typeof(T).IsValueType into a constant when generating code in the interpreter (does this happen already? I can't tell for sure), then do DCE based on it to reduce the amount of code in the method. This doesn't seem easy to me.
  • Move the comparison against null to only happen after we've checked whether T is a ValueType. The comparisons against null cause the value to be boxed.
  • Introduce a GenericValueTypeEqualityComparer. Its equals method will not contain boxing (much faster!) and will be smaller, which might cause it to get inlined. It does already have AggressiveInlining applied to it, but it's not inlined in this case.
  • Define a special comparer for int, like we already did for byte. This might also allow the comparison to get inlined here, and int is a pretty common type to use as a key or value in containers so it makes sense to have a dedicated comparer for it.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions