Skip to content

Optimize Half comparison operators and CompareTo - #131297

Merged
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:tannergooding-optimize-half-compareto
Jul 24, 2026
Merged

Optimize Half comparison operators and CompareTo#131297
tannergooding merged 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

@tannergooding
tannergooding merged commit 186b7ac into dotnet:main Jul 24, 2026
140 of 143 checks passed
@tannergooding
tannergooding deleted the tannergooding-optimize-half-compareto branch July 24, 2026 21:35
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
Fixes dotnet#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.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-rc1 milestone Jul 26, 2026
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

3 participants