Skip to content

Ark-ff Small Field Support#1044

Open
benbencik wants to merge 58 commits intoarkworks-rs:masterfrom
benbencik:small_fp
Open

Ark-ff Small Field Support#1044
benbencik wants to merge 58 commits intoarkworks-rs:masterfrom
benbencik:small_fp

Conversation

@benbencik
Copy link

@benbencik benbencik commented Oct 8, 2025

Description

The primary motivation of this PR is to create a generalized path toward vectorized/ SIMD instruction optimizations for finite fields in Arkworks.

In the process, we pick up a non-trivial performance boost in serial.

  • We introduce SmallFp, a procedural macro for instantiating prime fields with modulus < 2^127
  • In the place of BigInt, this macro uses native integer types (u8, u16, u32, u64, u128)
  • For a summary of results and benchmarking please see section "results" in slides
  • There a no breaking changes in this effort

SLIDES: https://andrewzitek.xyz/images/small_fp_slides.pdf

Closes: #1038

This work was done in collaboration with @z-tech

@benbencik benbencik marked this pull request as ready for review October 9, 2025 10:07
@benbencik benbencik requested review from a team as code owners October 9, 2025 10:07
@benbencik benbencik requested review from Pratyush and z-tech and removed request for a team October 9, 2025 10:07
@weikengchen
Copy link
Member

We probably will find a way to bypass the nightly compilation failures in another PR... so ignore that for now
One way is to add unused_unsafe for now.

@weikengchen
Copy link
Member

oh there is already a PR in the work for that: #1059 (comment)

@benbencik
Copy link
Author

After meeting with @Pratyush and @z-tech, we agreed it would be meaningful to merge the SmallField into the current macro. This way, the macro would pick the most suitable implementation based on the modulus size (fits into native type -> SmallFp, otherwise -> Fp). Naively, we can reuse the same macro MontConfig as follows:

#[derive(MontConfig)]
#[modulus = "2147483647"]
#[generator = "7"]
pub struct SmallFp32ConfigM31;
pub type SmallFp32M31 = SmallFp<SmallFp32ConfigM31>;

However, the user still needs to explicitly state what type they want to use. To make it "automatic", we might create a new OptimalFp type that wraps around. But this might be unnecessarily complex.

Instead, we propose a simple macro define_field!(modulus = .., generator = ..., name = ...); which expands to the derive macro used previously. This does not introduce any breaking change in the project. What do you think about it? Do you have any suggestions?

@z-tech
Copy link
Contributor

z-tech commented Mar 13, 2026

Hi, @Pratyush @weikengchen many people I speak with are excited to start using this and I have a growing chain of unmerged code from projects that are blocked by this PR.

Spongefish needs to specifically support small fields which requires changes in Crypto Primitives and Efficient Sumcheck needs to bump Spongefish and both of these are essential for downstream implementations like Warp. There is a healthy number of follow up projects pending that involve all five of these repos.

Can we please set a deadline for merging?

@weikengchen
Copy link
Member

I am 12 files away :)

(I was only 6 files away four months ago but there are new changes)

let mut e = generator;
for _i in 0..adicity {
e = e.modpow(&base.into(), &modulus)
}
Copy link
Member

Choose a reason for hiding this comment

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

I think you forget to check something here?

1u128 << k_bits
};
let r_mod_n = if k_bits == 128 {
(((1u128 << 127) % modulus) + ((1u128 << 127) % modulus)) % modulus
Copy link
Member

Choose a reason for hiding this comment

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

how sure are we that would work for prime that is larger than 1 << 127?

In those cases, it still has (1 << 127) + (1 << 127) = 1 << 128 causing an overflow?

fn mod_inverse_pow2(n: u128, k_bits: u32) -> u128 {
let mut inv = 1u128;
for _ in 0..k_bits {
inv = inv.wrapping_mul(2u128.wrapping_sub(n.wrapping_mul(inv)));
Copy link
Member

Choose a reason for hiding this comment

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

If it is convenient, can you point me to why it was implemented this way? (It is not obvious)

Copy link
Member

@weikengchen weikengchen left a comment

Choose a reason for hiding this comment

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

Check the final feedback and we can get this wrapped up

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.

4 participants