#curve #optimized #aarch64 #x86-64 #low-level #arithmetic #pasta

sys semolina

Optimized field arithmetic for Pasta moduli for x86-64 and aarch64

5 releases

0.1.4 May 17, 2023
0.1.3 May 5, 2023
0.1.2 Jun 25, 2022
0.1.1 Jun 9, 2022
0.1.0 May 19, 2022

#415 in Math

Download history 922/week @ 2024-01-03 841/week @ 2024-01-10 608/week @ 2024-01-17 480/week @ 2024-01-24 524/week @ 2024-01-31 863/week @ 2024-02-07 1071/week @ 2024-02-14 731/week @ 2024-02-21 699/week @ 2024-02-28 645/week @ 2024-03-06 668/week @ 2024-03-13 471/week @ 2024-03-20 398/week @ 2024-03-27 345/week @ 2024-04-03 434/week @ 2024-04-10 305/week @ 2024-04-17

1,549 downloads per month
Used in 10 crates (2 directly)

Apache-2.0

585KB
25K SLoC

GNU Style Assembly 14K SLoC // 0.0% comments Assembly 5K SLoC Perl 5K SLoC // 0.1% comments Rust 619 SLoC // 0.0% comments C 93 SLoC // 0.2% comments Shell 19 SLoC

Semolina

Even though Pasta Curves name's etymology is astronomical, it sounds too gastronomical to see past it. Hence the name, Semolina, the main ingridient in making pasta. The library is a collection of low-level x86_64 and aarch64 primitives optimized for Pasta moduli. It currently provides basic arithmetic, conversion, exponentiation helper and modular inversion subroutines. cargo test exercises these against Python. No benchmarks are provided here, because it's argued that it makes more sense to benchmark higher level implementations.

Technical ranting[s]

From performance viewpoint following implementation is optimal, because it compiles as straightforward call to pasta_mul with return value's address as destination pointer:

impl core::ops::Mul for $field {
    type Output = Self;

    fn mul(self, other: Self) -> Self {
        unsafe {
            let mut out = MaybeUninit::<Self>::uninit().assume_init();
            pasta_mul(&mut out.0, &self.0, &other.0, &$mod, $m0);
            out
        }
    }
}

However, it's argued that using assume_init() as initial assignment is not safe in Rust, and that it should be used rather upon return from the subroutine. Which is compiled as a) allocate a temporary value on the stack, b) call pasta_mul with the temporary value's address as destination pointer, c) finally copy the temporary value to the target location. This is arguably inefficient. The below snippet on the other hand is compiled as a) zero the return value, b) call pasta_mul with the return value's address as the destination pointer. Which is why it was chosen, for efficiency.

impl core::ops::Mul for $field {
    type Output = Self;

    fn mul(self, other: Self) -> Self {
        let mut out = Self::default();
        unsafe { pasta_mul(&mut out.0, &self.0, &other.0, &$mod, $m0) };
        out
    }
}

License

The semolina library is licensed under the Apache License Version 2.0 software license.

Dependencies