Skip to content

[repo-assist] perf: optimise Ceiling/Floor/Truncate and add string benchmarks - #209

Open
github-actions[bot] wants to merge 5 commits into
mainfrom
repo-assist/perf-floor-ceil-inlining-2026-06-30-5d3eb8fea7993bd2
Open

[repo-assist] perf: optimise Ceiling/Floor/Truncate and add string benchmarks#209
github-actions[bot] wants to merge 5 commits into
mainfrom
repo-assist/perf-floor-ceil-inlining-2026-06-30-5d3eb8fea7993bd2

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

🤖 This PR was created by Repo Assist, an automated AI assistant.

Summary

Small, surgical performance improvements to the core math helpers, plus new benchmarks covering string operations that were previously untracked.

Changes

src/FixedPointNano/FixedPointNano.cs

Ceiling and Floor — eliminate the final multiply

The previous implementation computed quotient and remainder separately, then reconstructed the result via quotient * Scale. The new versions compute only the modulo and reconstruct via subtraction:

// Before (Ceiling)
var quotient = value.RawValue / Scale;
var remainder = value.RawValue % Scale;
if (remainder > 0) quotient = checked(quotient + 1);
return new FixedPointNano(checked(quotient * Scale));

// After (Ceiling) — one modulo, one conditional subtract
var remainder = value.RawValue % Scale;
return remainder <= 0
    ? new FixedPointNano(value.RawValue - remainder)
    : new FixedPointNano(checked(value.RawValue - remainder + Scale));

On x64, div gives both quotient and remainder in one instruction, so the JIT was already fusing the two operations. The gain here is eliminating the imul that followed.

Truncate — same pattern

// Before: (raw / Scale) * Scale
// After:  raw - (raw % Scale)

Replaces a multiply with a subtract.

AggressiveInlining added to: FractionalPart, IsInteger, Frac, Max

These one-liner helpers were the only public methods in the hot-path category without the hint. The JIT already inlines them in most cases, but the attribute prevents de-optimization under PGO tier changes.

benchmarks/FixedPointNano.Benchmarks/FixedPointNanoMathBenchmarks.cs

Added 6 new benchmarks:

Benchmark Measures
ToStringRaw FixedPointNano.ToString()
ToStringDecimalReference decimal.ToString() for comparison
TryFormatRaw ISpanFormattable.TryFormat
TryFormatDecimalReference decimal.TryFormat for comparison
ParseRaw FixedPointNano.Parse
TryParseRaw FixedPointNano.TryParse

String formatting and parsing had no representation in the benchmark suite — an important gap for a type used in financial logging pipelines.

Trade-offs

  • Semantics are identical; all 1440 existing tests pass.
  • Ceiling/Floor/Truncate use checked arithmetic for overflow safety (same as before).
  • AggressiveInlining on Max / FractionalPart etc. carries a marginal code-size cost at large call-sites, but these are all trivial single-expression bodies where the trade-off clearly favours inlining.

Test Status

dotnet build FixedPointNano.slnx -c Release
Build succeeded. 0 Warning(s), 0 Error(s)

dotnet test tests/FixedPointNano.Tests -c Release
Passed! - Failed: 0, Passed: 1440, Skipped: 0, Total: 1440

Build: ✅ Tests: ✅ (1440 passing, 0 failures)

Generated by 🌈 Repo Assist, see workflow run.

Generated by 🌈 Repo Assist, see workflow run. Learn more.

Add this agentic workflows to your repo

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@e15e57b40918dbca11b350c55d02ab61934afa75

github-actions Bot and others added 4 commits June 30, 2026 13:45
…erfaces (#187)

FixedPointNano already had Parse/TryParse methods and MaxValue/MinValue
properties. This commit formally declares the three interfaces on the
struct so consumers can use generic constraints against them.

Also adds 11 tests covering Parse, TryParse, and the new interface
contracts via generic helper methods.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…#193)

* feat: add IsPositive, IsNegative, and IsZero predicates

Add three AggressiveInlining static predicates that complement the
existing IsInteger helper and complete the sign/zero API:

  - IsPositive(value): true when RawValue > 0
  - IsNegative(value): true when RawValue < 0
  - IsZero(value):     true when RawValue == 0

These are commonly expected on numeric types and lay the groundwork
for implementing INumber<T>/ISignedNumber<T> in a future PR.
3 new tests; total 1440, all passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Nich Overend <nich@nixnet.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Moq was used in exactly one test (ToStringAndTryFormatShouldUse-
ProvidedFormatProvider) solely to stub a trivial IFormatProvider.
Replace it with a lightweight private TrackingFormatProvider helper
that returns the configured NumberFormatInfo and records call count.

This eliminates a transitive dependency on Castle.Core and any other
Moq internals from the test project, reducing the dependency surface
without any loss of test coverage.

All 1437 tests pass; 0 warnings, 0 errors.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ring benchmarks

- Ceiling/Floor: replace quotient*Scale multiply with subtraction
  (one modulo instead of div+mod+mul per call)
- Truncate: replace (raw/Scale)*Scale with raw-(raw%Scale)
  (eliminates the multiply)
- FractionalPart, IsInteger, Frac, Max: add AggressiveInlining
- Benchmarks: add ToStringRaw, TryFormatRaw, ParseRaw, TryParseRaw
  and their decimal reference counterparts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@NichUK
NichUK marked this pull request as ready for review July 12, 2026 04:53
Copilot AI review requested due to automatic review settings July 12, 2026 04:53

Copilot AI left a comment

Copy link
Copy Markdown

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 targets small performance improvements in FixedPointNano math helpers and expands the benchmark/test surface to cover string formatting/parsing-related APIs.

Changes:

  • Optimizes Ceiling, Floor, and Truncate to avoid an extra multiply by using modulo/subtraction.
  • Extends FixedPointNano’s API surface with sign/zero helpers and generic parsing/min-max interfaces, plus corresponding tests.
  • Adds new BenchmarkDotNet cases for ToString, TryFormat, Parse, and TryParse.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/FixedPointNano/FixedPointNano.cs Updates core rounding helpers and adds sign/zero helpers + parsing/min-max interfaces.
benchmarks/FixedPointNano.Benchmarks/FixedPointNanoMathBenchmarks.cs Adds string format/parse benchmarks to broaden perf tracking.
tests/FixedPointNano.Tests/FixedPointNanoTests.cs Adds tests for new helpers and parsing; replaces Moq-based provider verification with a local tracking provider.
tests/FixedPointNano.Tests/FixedPointNano.Tests.csproj Removes Moq dependency and adjusts test package references (but currently conflicts with central package version management).
Comments suppressed due to low confidence (1)

src/FixedPointNano/FixedPointNano.cs:281

  • The PR description highlights adding AggressiveInlining to hot-path helpers, but this diff also removes the attribute from several previously-inlined methods (e.g., Sign, and elsewhere CopySign, Deconstruct, ToDecimal, unary +, ++, --). If the intent is to keep/expand inlining hints for perf, these removals are contradictory; if the removals are intentional, the PR description should be updated to reflect that trade-off.
    public static int Sign(FixedPointNano value)
    {
        return value.RawValue switch
        {
            > 0 => 1,
            < 0 => -1,
            _ => 0,
        };

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 8 to 12
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="coverlet.msbuild" Version="8.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Copilot AI requested a review from NichUK July 12, 2026 04:57
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.

2 participants