Skip to content

Optimize Half comparison operators and CompareTo#131297

Open
tannergooding wants to merge 1 commit into
dotnet:mainfrom
tannergooding:tannergooding-optimize-half-compareto
Open

Optimize Half comparison operators and CompareTo#131297
tannergooding wants to merge 1 commit into
dotnet:mainfrom
tannergooding:tannergooding-optimize-half-compareto

Conversation

@tannergooding

Copy link
Copy Markdown
Member

Fixes #43117.

Half.CompareTo(Half) previously delegated to this < other, this > other, and this == other. Since those operators were rewritten to handle signed values and zeros, each is now a full managed method with its own NaN/sign/zero handling -- so the common non-NaN path ran roughly six IsNaN checks plus redundant sign/zero work before falling into a bit comparison it could have done directly.

This rewrites CompareTo to handle NaN once and then compare a monotonic sign-magnitude ordering key derived directly from the bits (both zeros collapse to the same key, preserving +0 == -0).


It also adds an AVX2 fast path to CompareTo, operator <, and operator <= that compares via (float)Half. On TARGET_XARCH + AVX2 the Half->float conversion lowers to a hardware vcvtph2ps, so comparing as float is materially cheaper. operator > / operator >= are thin right < left / right <= left wrappers and inherit the fast path automatically, and the existing Min/Max/*Magnitude* helpers already delegate through float/relational operators, so they benefit as well. Avx2.IsSupported is a JIT-time constant, so the non-taken branch is eliminated and there's no overhead on other targets; the bit-based logic remains as the hardware-independent fallback.

The equality operators (==, !=, Equals) are intentionally left on the bit logic -- converting to float there measured slower, since the bit equality check is nearly free and doesn't justify two conversions.

Half->float is lossless and order-preserving (NaN and signed-zero semantics preserved), so all fast paths match the existing bit semantics exactly.


Measurements (local, AVX2 machine; 4096 comparisons/op; in-process BenchmarkDotNet)

CompareTo, mixed data:

Impl Time ns/cmp
Old (operator-chained) 13.06 us ~3.19
Bit-key only 7.10 us ~1.73
Bit-key + AVX2 float path 3.47 us ~0.85

operator <: 4.76 us -> 2.41 us (~2x).

With AVX2 disabled the change is still a win over the old code (CompareTo 12.25 -> 7.06 us via the bit-key fallback), and the equality path measured slower as float (2.16 -> 3.20 us), which is why it was left alone.

Tests: extended HalfTests.CompareTo_TestData with signed-zero, subnormal-sign, and NaN/infinity ordering cases. Full HalfTests (1629 cases) pass both with AVX2 enabled and with DOTNET_EnableAVX2=0 exercising the fallback.

Note

This PR description and the code changes were drafted with the assistance of GitHub Copilot.

Replace the operator-chained CompareTo with a single NaN check plus a monotonic sign-magnitude ordering key, and add an AVX2 fast path to CompareTo, operator <, and operator <= that compares via (float)Half, which lowers to a hardware vcvtph2ps conversion. The relational operators funnel > and >= through the fast path as well, while the equality operators stay on the cheaper bit logic. Equality is left unchanged since converting to float measured slower there.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 23, 2026 22:35
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR optimizes System.Half comparison behavior by (1) rewriting Half.CompareTo(Half) to avoid operator chaining and redundant NaN/sign/zero checks and (2) adding an AVX2-gated fast path for <, <=, and CompareTo that compares via lossless (float)Half conversion.

Changes:

  • Update Half.CompareTo(Half) to handle NaN up-front and otherwise compare via a monotonic, bit-derived ordering key (collapsing +0/-0).
  • Add AVX2 fast paths for operator <, operator <=, and CompareTo(Half) that compare through float on supported targets.
  • Extend HalfTests CompareTo_TestData with additional signed-zero, subnormal-sign, and NaN/infinity ordering cases.
Show a summary per file
File Description
src/libraries/System.Private.CoreLib/src/System/Half.cs Reworks CompareTo(Half) to avoid operator chaining and adds AVX2-gated float-based fast paths for <, <=, and CompareTo.
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs Adds extra CompareTo test vectors covering signed zero, subnormal sign ordering, and NaN vs infinity ordering.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 0

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics
See info in area-owners.md if you want to be subscribed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optimize Half.CompareTo

2 participants