Skip to content

perf(math): Add #[inline] attributes to hot field arithmetic paths#1128

Merged
diegokingston merged 2 commits intomainfrom
perf/inline-attributes
Feb 3, 2026
Merged

perf(math): Add #[inline] attributes to hot field arithmetic paths#1128
diegokingston merged 2 commits intomainfrom
perf/inline-attributes

Conversation

@diegokingston
Copy link
Collaborator

Add inline attributes to performance-critical field arithmetic functions to improve optimization opportunities for the compiler:

  • element.rs: Add #[inline] to From conversions, PartialEq, Div trait implementations, Default, from_raw(), and to_raw()

  • mersenne31/field.rs: Add #[inline(always)] to all IsField and IsPrimeField implementations including add, sub, mul, neg, inv, div, eq, zero, one, from_u64, from_base_type, double, representative. Also add inline to helper methods weak_reduce, as_representative, mul_power_two, pow_2, two_square_minus_one.

  • u64_goldilocks_field.rs: Add #[inline] to inv and div for both Goldilocks64Field and Degree2GoldilocksExtensionField, plus #[inline(always)] to eq, zero, one for the extension field.

Add inline attributes to performance-critical field arithmetic functions
to improve optimization opportunities for the compiler:

- element.rs: Add #[inline] to From conversions, PartialEq, Div trait
  implementations, Default, from_raw(), and to_raw()

- mersenne31/field.rs: Add #[inline(always)] to all IsField and
  IsPrimeField implementations including add, sub, mul, neg, inv, div,
  eq, zero, one, from_u64, from_base_type, double, representative.
  Also add inline to helper methods weak_reduce, as_representative,
  mul_power_two, pow_2, two_square_minus_one.

- u64_goldilocks_field.rs: Add #[inline] to inv and div for both
  Goldilocks64Field and Degree2GoldilocksExtensionField, plus
  #[inline(always)] to eq, zero, one for the extension field.

These inline hints help the compiler make better inlining decisions
for small, frequently-called functions in hot loops like FFT and
polynomial operations.
@diegokingston diegokingston requested a review from a team as a code owner February 3, 2026 13:35
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code review by ChatGPT

#[inline(always)]
fn representative(x: &u32) -> u32 {
debug_assert!((x >> 31) == 0);
Self::as_representative(x)
Copy link

Choose a reason for hiding this comment

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

Correctness

  • The mul_power_two implementation appears to be incorrectly handling the shifting of bits, leading to possible incorrect multiplication results when the bits carried exceed the maximum allowed.
  • The function pow_2 should ensure that squaring does not overflow, as continued squaring can exceed the 31-bit expectation.

Security

  • Functions like inv should ensure no secret-dependent branching for side-channel prevention; similar care is needed with functions like mul, sub, and neg for consistent timing.
  • The as_representative, neg, and inv methods may benefit from checking their handling of inputs like zero and the prime field order.

Performance

  • Excessive #[inline(always)] attributes may lead to code bloat; use them judiciously, especially for complex logic.
  • Review the mul conversion from u32 to u64 and back to ensure minimal overhead and evaluate if it's performant enough.

Bugs & Errors

  • The sub function uses a subtraction which could underflow if a < b.
  • Potential overflows in arithmetic functions considering operations return values larger than 31 bits which may lead to crashes.

Code Simplicity

  • The mul_power_two implementation lacks clarity and would benefit from comments or a simplified approach.
  • Multiple uses of weak_reduce imply a potential for consolidation or more straightforward flow; repeated usage may cover duplicated logic.

Additional Notes

  • Ensure rigorous testing for edge cases, such as successive operations leading to zero or the prime field order, especially in reduced field context.
  • Consider adding comments or documentation to clarify assumptions, such as when bitwise operations are safe and why certain methods may not need full reductions.

Due to these concerns in correctness, security, performance, and potential bugs, this code is not ready to merge. Further refinement and testing are required to ensure all conditions are safely managed and optimized.

#[inline(always)]
fn one() -> Self::BaseType {
[FpE::one(), FpE::zero()]
}
Copy link

Choose a reason for hiding this comment

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

Correctness:

  • inv function: The handling of zero in inv seems correct, as it returns an error when the input is zero, which is an expected behavior to prevent division by zero.

Security:

  • There is no indication in the provided snippet whether the operations are constant-time. Ensure that modular arithmetic and field operations are constant-time to prevent timing side-channel attacks.
  • Make sure that sensitive data is properly zeroized after use. I couldn't see if and where sensitive data is zeroized from the snippet.
  • There's no detail about randomness in this snippet, so it should be secured elsewhere if used.
  • Ensure hash function domain separation where applicable.

Performance:

  • Inlining: Adding #[inline] and #[inline(always)] hints is good for performance, but be cautious as #[inline(always)] can sometimes lead to code bloat, though this seems appropriate here.
  • Multiplicative Inverse: The implementation uses addition chains for inverse calculation, which is often performance-optimal.

Bugs & Errors:

  • Error handling: The usage of Result types for inversion and division functions is appropriate to handle errors such as division by zero.
  • Potential Panics/Unwraps: There are no obvious uses of unwrap from the snippet provided. Ensure any other parts of the code do not invoke unwrap without checks.
  • Memory Safety: Without the broader context, there's nothing that appears unsafe here, but ensure all buffer/array accesses are bounds-checked.

Code Simplicity:

  • The code is concise and focuses on using existing utility functions like inv and mul, which promotes readability and maintainability.
  • Using descriptive names like norm_inv, a0_sq, and a1_sq aids in understanding the algorithm.

Recommendation:

  • Ensure you perform all cryptographic operations, especially those involving secrets, in constant time.
  • Double-check that all potential error conditions are appropriately handled, especially when the code relies on external inputs or parameters.
  • Make sure sensitive data is zeroed out after use, especially in areas handling private keys or other secrets.

@github-actions
Copy link

github-actions bot commented Feb 3, 2026

Kimi AI Review

The PR diff introduces several changes, primarily focused on adding #[inline] and #[inline(always)] attributes to various functions within the field/element.rs and field/fields/mersenne31/field.rs files. Here are my specific comments:

field/element.rs

  1. Addition of #[inline] attributes:

    • The addition of #[inline] to the from, from_raw, eq, and division methods is generally good practice for performance optimization, especially for small functions that are called frequently. However, it's important to consider the context and the actual performance gains. For cryptographic libraries, inlining can sometimes lead to increased code size and potential security issues like side-channel attacks if not handled carefully.

    • Line 120, 132, 159, 173, 399, 412, 424, 436, 472, 545: The use of #[inline] is appropriate here, but consider whether #[inline(always)] might be too aggressive, especially for methods that might not always benefit from being inlined (e.g., from_raw which is a simple constructor).

  2. Performance Considerations:

    • Line 399, 412, 424, 436: The division methods are marked as #[inline]. Given that division is typically a more expensive operation, it's worth profiling to ensure that inlining these does not negatively impact performance due to increased code size or cache usage.

field/fields/mersenne31/field.rs

  1. Addition of #[inline(always)] attributes:

    • Line 16, 27, 44, 58, 76, 84, 117: The use of #[inline(always)] on these functions is aggressive and should be justified. For cryptographic functions, especially those involving modular arithmetic, inlining can sometimes lead to timing side-channels if not carefully managed.
  2. Security Considerations:

    • Line 16, 27, 44, 58, 76, 84, 117: The aggressive inlining of cryptographic functions can potentially expose sensitive data through timing side-channels. It's crucial to ensure that these optimizations do not compromise the security model of the library.
  3. Code Simplicity and Maintainability:

    • Line 16, 27, 44, 58, 76, 84, 117: The addition of #[inline(always)] can make the code harder to maintain and understand, especially for new developers or external contributors who might not be familiar with Rust's inlining behavior.

field/fields/u64_goldilocks_field.rs

  1. Addition of #[inline] attributes:

    • Line 132, 141, 415, 424: Similar to the previous files, the use of #[inline] is appropriate, but the decision to use #[inline] vs #[inline(always)] should be based on profiling and understanding the specific performance characteristics of these functions.
  2. Performance and Security Considerations:

    • Line 132, 141, 415, 424: As with the previous files, consider the potential impact on performance and security. Inlining can be beneficial, but it should be done judiciously, especially in cryptographic contexts.

General Recommendations:

  • Profiling and Benchmarking: Before and after inlining, it's crucial to profile the code to understand the actual performance impact. This will help justify the use of #[inline] and #[inline(always)] attributes.
  • Security Audits: Given the cryptographic nature of the library, any changes that could potentially affect security, such as aggressive inlining, should be audited for potential side-channel vulnerabilities.
  • Documentation: It would be helpful to include documentation explaining why #[inline] and #[inline(always)] were chosen for specific functions, especially if they are not immediately obvious.

Overall, the changes aim to optimize performance, but it's important to balance these optimizations with security and maintainability considerations.

@greptile-apps
Copy link

greptile-apps bot commented Feb 3, 2026

Greptile Overview

Greptile Summary

This PR adds #[inline] and #[inline(always)] attributes to performance-critical field arithmetic operations across three key files to enable better compiler optimizations for hot paths in cryptographic computations.

Changes:

  • element.rs: Added #[inline] to trait implementations (From, PartialEq, Div, Default) and #[inline(always)] to from_raw()/to_raw() utility methods
  • mersenne31/field.rs: Comprehensively added #[inline(always)] to all IsField/IsPrimeField trait methods (add, mul, sub, neg, inv, div, eq, zero, one, from_u64, etc.) and helper functions
  • u64_goldilocks_field.rs: Added #[inline] to inv/div for both base and extension fields, plus #[inline(always)] to eq/zero/one for extension field

Minor inconsistency: Extension field's from_u64, from_base_type, and double methods lack inline attributes while the base field has #[inline(always)] on these methods.

Confidence Score: 4.5/5

  • Safe to merge with minimal risk - adds only compiler optimization hints without changing logic
  • Changes are purely additive compiler hints that don't alter functionality. Minor inconsistency in extension field inline coverage slightly reduces score from 5 to 4.5
  • Consider completing inline attribute coverage in u64_goldilocks_field.rs extension field methods for consistency

Important Files Changed

Filename Overview
crates/math/src/field/element.rs Added inline attributes to trait implementations (From, PartialEq, Div, Default) and utility methods (from_raw, to_raw) to improve compiler optimization for hot paths
crates/math/src/field/fields/mersenne31/field.rs Added inline(always) to all IsField/IsPrimeField trait methods and helper functions to optimize hot arithmetic paths for Mersenne31 field operations
crates/math/src/field/fields/u64_goldilocks_field.rs Added inline attributes to inv/div for base and extension fields, and inline(always) to eq/zero/one for extension field, though some methods remain without inline

Sequence Diagram

sequenceDiagram
    participant User as User Code
    participant FE as FieldElement
    participant Field as Field Implementation
    participant Compiler as Rust Compiler

    Note over User,Compiler: Hot Path Field Arithmetic

    User->>FE: element1 + element2
    activate FE
    FE->>Field: F::add(&val1, &val2)
    activate Field
    Note over Field: inline(always) forces inlining
    Field-->>FE: result (inlined)
    deactivate Field
    FE-->>User: FieldElement (optimized)
    deactivate FE

    User->>FE: element.inv()
    activate FE
    Note over FE: inline(always) wrapper
    FE->>Field: F::inv(&value)
    activate Field
    Note over Field: inline allows compiler discretion
    Field->>Field: Complex inv logic
    Field-->>FE: Result value
    deactivate Field
    FE-->>User: Result FieldElement
    deactivate FE

    User->>FE: From trait conversion
    activate FE
    Note over FE: inline on From impl
    FE->>Field: F::from_base_type(x)
    activate Field
    Note over Field: inline(always) on field method
    Field-->>FE: converted value (fully inlined)
    deactivate Field
    FE-->>User: FieldElement (zero overhead)
    deactivate FE

    Note over User,Compiler: Result: Reduced call overhead, better register allocation
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link

greptile-apps bot commented Feb 3, 2026

Additional Comments (1)

crates/math/src/field/fields/u64_goldilocks_field.rs
Missing #[inline(always)] on from_u64, from_base_type, and double for consistency with base Goldilocks64Field (lines 170-189) and to complete hot path optimization

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/math/src/field/fields/u64_goldilocks_field.rs
Line: 451:461

Comment:
Missing `#[inline(always)]` on `from_u64`, `from_base_type`, and `double` for consistency with base `Goldilocks64Field` (lines 170-189) and to complete hot path optimization

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@codecov-commenter
Copy link

codecov-commenter commented Feb 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.33%. Comparing base (9fc8541) to head (94c4c0e).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1128   +/-   ##
=======================================
  Coverage   71.33%   71.33%           
=======================================
  Files         166      166           
  Lines       36072    36072           
=======================================
  Hits        25732    25732           
  Misses      10340    10340           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link

github-actions bot commented Feb 3, 2026

Kimi AI Review

Here are my feedback and comments on the PR diff:

  1. FieldElement::from:

    • The #[inline] attribute on line 122 is unnecessary as from is a simple wrapper function that can be inlined by the compiler. Consider removing it to reduce attribute clutter.
  2. FieldElement::from_raw:

    • The #[inline(always)] attribute on line 162 is unnecessary for this simple function. Consider removing it to avoid premature optimization.
  3. Mersenne31Field::weak_reduce:

    • The #[inline(always)] attribute on line 22 is unnecessary for this small function. Consider removing it.
  4. Mersenne31Field::as_representative:

    • The #[inline(always)] attribute on line 32 is unnecessary for this small function. Consider removing it.
  5. Mersenne31Field::mul_power_two:

    • The #[inline(always)] attribute on line 48 is unnecessary for this small function. Consider removing it.
  6. Mersenne31Field::pow_2:

    • The #[inline] attribute on line 54 is unnecessary for this small function. Consider removing it.
  7. Mersenne31Field::two_square_minus_one:

    • The #[inline(always)] attribute on line 62 is unnecessary for this small function. Consider removing it.
  8. Mersenne31Field::add:

    • The #[inline(always)] attribute on line 80 is unnecessary for this small function. Consider removing it.
  9. Mersenne31Field::mul:

    • The #[inline(always)] attribute on line 88 is unnecessary for this small function. Consider removing it.
  10. Mersenne31Field::sub:

    • The #[inline(always)] attribute on line 94 is unnecessary for this small function. Consider removing it.
  11. Mersenne31Field::neg:

    • The #[inline(always)] attribute on line 100 is unnecessary for this small function. Consider removing it.
  12. Mersenne31Field::inv:

    • The #[inline] attribute on line 110 is unnecessary for this small function. Consider removing it.
  13. Mersenne31Field::div:

    • The #[inline] attribute on line 122 is unnecessary for this small function. Consider removing it.
  14. Mersenne31Field::eq:

    • The #[inline(always)] attribute on line 126 is unnecessary for this small function. Consider removing it.
  15. Mersenne31Field::zero:

    • The #[inline(always)] attribute on line 130 is unnecessary for this small function. Consider removing it.
  16. Mersenne31Field::one:

    • The #[inline(always)] attribute on line 134 is unnecessary for this small function. Consider removing it.
  17. Mersenne31Field::from_u64:

    • The #[inline(always)] attribute on line 140 is unnecessary for this small function. Consider removing it.
  18. Mersenne31Field::from_base_type:

    • The #[inline(always)] attribute on line 146 is unnecessary for this small function. Consider removing it.
  19. Mersenne31Field::double:

    • The #[inline(always)] attribute on line 152 is unnecessary for this small function. Consider removing it.
  20. Goldilocks64Field::inv:

    • The #[inline] attribute on line 138 is unnecessary for this small function. Consider removing it.
  21. Goldilocks64Field::div:

    • The #[inline] attribute on line 146 is unnecessary for this small function. Consider removing it.
  22. Degree2GoldilocksExtensionField::inv:

    • The #[inline] attribute on line 420 is unnecessary for this small function. Consider removing it.
  23. Degree2GoldilocksExtensionField::div:

    • The #[inline] attribute on line 428 is unnecessary for this small function. Consider removing it.
  24. Degree2GoldilocksExtensionField::eq:

    • The #[inline(always)] attribute on line 434 is unnecessary for this small function. Consider removing it.
  25. Degree2GoldilocksExtensionField::zero:

    • The #[inline(always)] attribute on line 440 is

@jotabulacios
Copy link
Contributor

Benched it with hyperfine (10M iterations of add/mul/sub loop) and
got:

  • Mersenne31: 197.7 ms → 164.7 ms (~20% faster)
  • Goldilocks: 164.5 ms → 160.3 ms (~3%)

@diegokingston diegokingston merged commit 06425ef into main Feb 3, 2026
11 checks passed
@diegokingston diegokingston deleted the perf/inline-attributes branch February 3, 2026 20:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants