Update to non-avx builds

This commit is contained in:
Pro7ech
2025-11-21 15:39:04 +01:00
parent 0fb88c9bd3
commit 3c818d292b
24 changed files with 356 additions and 128 deletions

View File

@@ -9,6 +9,9 @@ repository = "https://github.com/phantomzone-org/poulpy"
homepage = "https://github.com/phantomzone-org/poulpy"
documentation = "https://docs.rs/poulpy"
[features]
enable-avx = []
[dependencies]
poulpy-hal = {workspace = true}
poulpy-cpu-ref = {workspace = true}

View File

@@ -1,8 +1,51 @@
# 🐙 Poulpy-CPU-REF
# 🐙 Poulpy-CPU-AVX
**Poulpy-Backend-CPU-AVX** is a Rust crate that provides an AVX accelerated CPU implementation of **`poulpy-hal`**. This crate is used to instantiate projects implemented with **`poulpy-hal`**, **`poulpy-core`** and/or **`poulpy-schemes`**.
**Poulpy-CPU-AVX** is a Rust crate that provides an **AVX2 + FMA accelerated CPU backend for Poulpy**.
## Example
This backend implements the Poulpy HAL extension traits and can be used by:
- [`poulpy-hal`](https://github.com/phantomzone-org/poulpy/tree/main/poulpy-hal)
- [`poulpy-core`](https://github.com/phantomzone-org/poulpy/tree/main/poulpy-core)
- [`poulpy-schemes`](https://github.com/phantomzone-org/poulpy/tree/main/poulpy-schemes)
## 🚩 Safety and Requirements
To avoid illegal hardware instructions (SIGILL) on unsupported CPUs, this backend is **opt-in** and **only builds when explicitly requested**.
| Requirement | Status |
|------------|--------|
| Cargo feature flag | `--features enable-avx` **must be enabled** |
| CPU architecture | `x86_64` |
| CPU target features | `AVX2` + `FMA` |
If `enable-avx` is enabled but the target does not provide these capabilities, the build **fails immediately with a clear error message**, rather than generating invalid binaries.
When `enable-avx` is **not** enabled, this crate is simply skipped and Poulpy automatically falls back to the portable `poulpy-cpu-ref` backend. This ensure that Poulpy's workspace remains portable (e.g. for macOS ARM).
## ⚙️ Building with the AVX backend enabled
Because the compiler must generate AVX2 + FMA instructions, both the Cargo feature and CPU target flags must be specified:
```bash
RUSTFLAGS="-C target-feature=+avx2,+fma" \
cargo build --features enable-avx
````
### Running an example
```bash
RUSTFLAGS="-C target-feature=+avx2,+fma" \
cargo run --example <name> --features enable-avx
```
### Running benchmarks
```bash
RUSTFLAGS="-C target-feature=+avx2,+fma" \
cargo bench --features enable-avx
```
## Basic Usage
```rust
use poulpy_backend_cpu_avx::FFT64Avx;
@@ -12,7 +55,24 @@ let log_n: usize = 10;
let module = Module<FFT64Avx> = Module<FFT64Avx>::new(1<<log_n);
```
## Contributors
Once compiled with `enable-avx`, the backend is usable transparently anywhere Poulpy expects a backend type (`poulpy-hal`, `poulpy-core`, `poulpy-schemes`).
To add your own backend, implement the open extension traits from **`poulpy-hal/oep`** for a struct that implements the `Backend` trait.
This will automatically make your backend compatible with the API of **`poulpy-hal`**, **`poulpy-core`** and **`poulpy-schemes`**.
## 🤝 Contributors
To implement your own Poulpy backend (SIMD or accelerator):
1. Define a backend struct
2. Implement the open extension traits from `poulpy-hal/oep`
3. Implement the `Backend` trait
Your backend will automatically integrate with:
* `poulpy-hal`
* `poulpy-core`
* `poulpy-schemes`
No modifications to those crates are required — the HAL provides the extension points.
---
For questions or guidance, feel free to open an issue or discussion in the repository.

View File

@@ -1,11 +1,17 @@
#![cfg(target_arch = "x86_64")]
use std::hint::black_box;
use criterion::{Criterion, criterion_group, criterion_main};
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use poulpy_cpu_avx::{ReimFFTAvx, ReimIFFTAvx};
use poulpy_hal::reference::fft64::reim::{ReimDFTExecute, ReimFFTTable, ReimIFFTTable};
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma")))]
fn bench_ifft_avx2_fma(_c: &mut Criterion) {
eprintln!("Skipping: AVX IFft benchmark requires x86_64 + AVX2 + FMA");
}
#[cfg(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma"))]
pub fn bench_ifft_avx2_fma(c: &mut Criterion) {
use criterion::BenchmarkId;
use poulpy_cpu_avx::ReimIFFTAvx;
use poulpy_hal::reference::fft64::reim::{ReimDFTExecute, ReimIFFTTable};
use std::hint::black_box;
let group_name: String = "ifft_avx2_fma".to_string();
let mut group = c.benchmark_group(group_name);
@@ -41,7 +47,18 @@ pub fn bench_ifft_avx2_fma(c: &mut Criterion) {
group.finish();
}
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma")))]
fn bench_fft_avx2_fma(_c: &mut Criterion) {
eprintln!("Skipping: AVX FFT benchmark requires x86_64 + AVX2 + FMA");
}
#[cfg(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma"))]
pub fn bench_fft_avx2_fma(c: &mut Criterion) {
use criterion::BenchmarkId;
use poulpy_cpu_avx::ReimFFTAvx;
use poulpy_hal::reference::fft64::reim::{ReimDFTExecute, ReimFFTTable};
use std::hint::black_box;
let group_name: String = "fft_avx2_fma".to_string();
let mut group = c.benchmark_group(group_name);

View File

@@ -1,21 +1,36 @@
#![cfg(target_arch = "x86_64")]
// poulpy-backend/benches/vec_znx_add.rs
use criterion::{Criterion, criterion_group, criterion_main};
use poulpy_cpu_avx::FFT64Avx;
use poulpy_hal::reference::vec_znx::{bench_vec_znx_add, bench_vec_znx_automorphism, bench_vec_znx_normalize_inplace};
#[allow(dead_code)]
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma")))]
fn bench_vec_znx_add_cpu_avx_fft64(_c: &mut Criterion) {
eprintln!("Skipping: AVX IFft benchmark requires x86_64 + AVX2 + FMA");
}
#[cfg(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma"))]
fn bench_vec_znx_add_cpu_avx_fft64(c: &mut Criterion) {
bench_vec_znx_add::<FFT64Avx>(c, "FFT64Avx");
use poulpy_cpu_avx::FFT64Avx;
poulpy_hal::reference::vec_znx::bench_vec_znx_add::<FFT64Avx>(c, "FFT64Avx");
}
#[allow(dead_code)]
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma")))]
fn bench_vec_znx_normalize_inplace_cpu_avx_fft64(_c: &mut Criterion) {
eprintln!("Skipping: AVX IFft benchmark requires x86_64 + AVX2 + FMA");
}
#[cfg(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma"))]
fn bench_vec_znx_normalize_inplace_cpu_avx_fft64(c: &mut Criterion) {
bench_vec_znx_normalize_inplace::<FFT64Avx>(c, "FFT64Avx");
use poulpy_cpu_avx::FFT64Avx;
poulpy_hal::reference::vec_znx::bench_vec_znx_normalize_inplace::<FFT64Avx>(c, "FFT64Avx");
}
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma")))]
fn bench_vec_znx_automorphism_cpu_avx_fft64(_c: &mut Criterion) {
eprintln!("Skipping: AVX IFft benchmark requires x86_64 + AVX2 + FMA");
}
#[cfg(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma"))]
fn bench_vec_znx_automorphism_cpu_avx_fft64(c: &mut Criterion) {
bench_vec_znx_automorphism::<FFT64Avx>(c, "FFT64Avx");
use poulpy_cpu_avx::FFT64Avx;
poulpy_hal::reference::vec_znx::bench_vec_znx_automorphism::<FFT64Avx>(c, "FFT64Avx");
}
criterion_group!(

View File

@@ -1,27 +1,15 @@
// poulpy-backend/benches/vec_znx_add.rs
use criterion::{Criterion, criterion_group, criterion_main};
#[cfg(target_arch = "x86_64")]
use poulpy_cpu_avx::FFT64Avx;
#[cfg(not(target_arch = "x86_64"))]
use poulpy_cpu_ref::FFT64Ref;
use poulpy_hal::bench_suite::vmp::bench_vmp_apply_dft_to_dft;
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma")))]
fn bench_vmp_apply_dft_to_dft_cpu_avx_fft64(_c: &mut Criterion) {
eprintln!("Skipping: AVX IFft benchmark requires x86_64 + AVX2 + FMA");
}
#[cfg(target_arch = "x86_64")]
#[cfg(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma"))]
fn bench_vmp_apply_dft_to_dft_cpu_avx_fft64(c: &mut Criterion) {
bench_vmp_apply_dft_to_dft::<FFT64Avx>(c, "FFT64Avx");
}
#[cfg(not(target_arch = "x86_64"))]
fn bench_vmp_apply_dft_to_dft_cpu_ref_fft64(c: &mut Criterion) {
bench_vmp_apply_dft_to_dft::<FFT64Ref>(c, "FFT64Ref");
use poulpy_cpu_avx::FFT64Avx;
poulpy_hal::bench_suite::vmp::bench_vmp_apply_dft_to_dft::<FFT64Avx>(c, "FFT64Avx");
}
#[cfg(target_arch = "x86_64")]
criterion_group!(benches_x86, bench_vmp_apply_dft_to_dft_cpu_avx_fft64,);
#[cfg(not(target_arch = "x86_64"))]
criterion_group!(benches_ref, bench_vmp_apply_dft_to_dft_cpu_ref_fft64,);
#[cfg(target_arch = "x86_64")]
criterion_main!(benches_x86);
#[cfg(not(target_arch = "x86_64"))]
criterion_main!(benches_ref);

View File

@@ -1,8 +1,8 @@
use itertools::izip;
#[cfg(target_arch = "x86_64")]
#[cfg(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma"))]
use poulpy_cpu_avx::FFT64Avx as BackendImpl;
#[cfg(not(target_arch = "x86_64"))]
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma")))]
use poulpy_cpu_ref::FFT64Ref as BackendImpl;
use poulpy_hal::{
@@ -143,4 +143,3 @@ fn main() {
println!("{}: {} {}", i, a, (*b as f64) / scale);
});
}

View File

@@ -1,4 +1,19 @@
#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
// ─────────────────────────────────────────────────────────────
// Build the backend **only when ALL conditions are satisfied**
// ─────────────────────────────────────────────────────────────
#![cfg(all(feature = "enable-avx", target_arch = "x86_64", target_feature = "avx2", target_feature = "fma"))]
// If the user enables this backend but targets a non-x86_64 CPU → abort
#[cfg(all(feature = "enable-avx", not(target_arch = "x86_64")))]
compile_error!("feature `enable-avx` requires target_arch = \"x86_64\".");
// If the user enables this backend but AVX2 isn't enabled in the target → abort
#[cfg(all(feature = "enable-avx", target_arch = "x86_64", not(target_feature = "avx2")))]
compile_error!("feature `enable-avx` requires AVX2. Build with RUSTFLAGS=\"-C target-feature=+avx2\".");
// If the user enables this backend but FMA isn't enabled in the target → abort
#[cfg(all(feature = "enable-avx", target_arch = "x86_64", not(target_feature = "fma")))]
compile_error!("feature `enable-avx` requires FMA. Build with RUSTFLAGS=\"-C target-feature=+fma\".");
mod module;
mod reim;