Skip to content

Commit

Permalink
Merge pull request #523 from koute/main_runtime_simd
Browse files Browse the repository at this point in the history
Runtime backend autodetection
  • Loading branch information
rozbb authored Jun 11, 2023
2 parents 618c508 + 50aa635 commit e111b5d
Show file tree
Hide file tree
Showing 27 changed files with 976 additions and 558 deletions.
33 changes: 14 additions & 19 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,19 @@ jobs:
- run: cargo build --target thumbv7em-none-eabi --release
- run: cargo build --target thumbv7em-none-eabi --release --features serde

build-simd-nightly:
name: Build simd backend (nightly)
test-simd-native:
name: Test simd backend (native)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly
# Build with AVX2 features, then with AVX512 features
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx2'
run: cargo build --target x86_64-unknown-linux-gnu
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx512ifma'
run: cargo build --target x86_64-unknown-linux-gnu
# This will:
# 1) build all of the x86_64 SIMD code,
# 2) run all of the SIMD-specific tests that the test runner supports,
# 3) run all of the normal tests using the best available SIMD backend.
RUSTFLAGS: '-C target_cpu=native'
run: cargo test --features simd --target x86_64-unknown-linux-gnu

test-simd-avx2:
name: Test simd backend (avx2)
Expand All @@ -76,8 +76,10 @@ jobs:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx2'
run: cargo test --target x86_64-unknown-linux-gnu
# This will run AVX2-specific tests and run all of the normal tests
# with the AVX2 backend, even if the runner supports AVX512.
RUSTFLAGS: '-C target_feature=+avx2'
run: cargo test --no-default-features --features alloc,precomputed-tables,zeroize,simd_avx2 --target x86_64-unknown-linux-gnu

build-docs:
name: Build docs
Expand Down Expand Up @@ -131,12 +133,7 @@ jobs:
- uses: dtolnay/rust-toolchain@nightly
with:
components: clippy
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx2'
run: cargo clippy --target x86_64-unknown-linux-gnu
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx512ifma'
run: cargo clippy --target x86_64-unknown-linux-gnu
- run: cargo clippy --target x86_64-unknown-linux-gnu

rustfmt:
name: Check formatting
Expand All @@ -162,9 +159,7 @@ jobs:
- uses: dtolnay/[email protected]
- run: cargo build --no-default-features --features serde
# Also make sure the AVX2 build works
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx2'
run: cargo build --target x86_64-unknown-linux-gnu
- run: cargo build --target x86_64-unknown-linux-gnu

bench:
name: Check that benchmarks compile
Expand Down
17 changes: 15 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ rustdoc-args = [
"--html-in-header", "docs/assets/rustdoc-include-katex-header.html",
"--cfg", "docsrs",
]
rustc-args = ["--cfg", "curve25519_dalek_backend=\"simd\""]
features = ["serde", "rand_core", "digest", "legacy_compatibility"]

[dev-dependencies]
Expand All @@ -54,15 +53,29 @@ digest = { version = "0.10", default-features = false, optional = true }
subtle = { version = "2.3.0", default-features = false }
serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] }
zeroize = { version = "1", default-features = false, optional = true }
unsafe_target_feature = { version = "= 0.1.1", optional = true }

[target.'cfg(target_arch = "x86_64")'.dependencies]
cpufeatures = "0.2.6"

[target.'cfg(curve25519_dalek_backend = "fiat")'.dependencies]
fiat-crypto = "0.1.19"

[features]
default = ["alloc", "precomputed-tables", "zeroize"]
default = ["alloc", "precomputed-tables", "zeroize", "simd"]
alloc = ["zeroize?/alloc"]
precomputed-tables = []
legacy_compatibility = []

# Whether to allow the use of the AVX2 SIMD backend.
simd_avx2 = ["unsafe_target_feature"]

# Whether to allow the use of the AVX512 SIMD backend.
# (Note: This requires Rust nightly; on Rust stable this feature will be ignored.)
simd_avx512 = ["unsafe_target_feature"]

# A meta-feature to allow all SIMD backends to be used.
simd = ["simd_avx2", "simd_avx512"]

[profile.dev]
opt-level = 2
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
FEATURES := serde rand_core digest legacy_compatibility

export RUSTFLAGS := --cfg=curve25519_dalek_backend="simd"
export RUSTDOCFLAGS := \
--cfg docsrs \
--html-in-header docs/assets/rustdoc-include-katex-header.html
Expand Down
50 changes: 25 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ curve25519-dalek = "4.0.0-rc.2"
| `alloc` || Enables Edwards and Ristretto multiscalar multiplication, batch scalar inversion, and batch Ristretto double-and-compress. Also enables `zeroize`. |
| `zeroize` || Enables [`Zeroize`][zeroize-trait] for all scalar and curve point types. |
| `precomputed-tables` || Includes precomputed basepoint multiplication tables. This speeds up `EdwardsPoint::mul_base` and `RistrettoPoint::mul_base` by ~4x, at the cost of ~30KB added to the code size. |
| `simd_avx2` || Allows the AVX2 SIMD backend to be used, if available. |
| `simd_avx512` || Allows the AVX512 SIMD backend to be used, if available. |
| `simd` || Allows every SIMD backend to be used, if available. |
| `rand_core` | | Enables `Scalar::random` and `RistrettoPoint::random`. This is an optional dependency whose version is not subject to SemVer. See [below](#public-api-semver-exemptions) for more details. |
| `digest` | | Enables `RistrettoPoint::{from_hash, hash_from_bytes}` and `Scalar::{from_hash, hash_from_bytes}`. This is an optional dependency whose version is not subject to SemVer. See [below](#public-api-semver-exemptions) for more details. |
| `serde` | | Enables `serde` serialization/deserialization for all the point and scalar types. |
Expand Down Expand Up @@ -95,30 +98,26 @@ See tracking issue: [curve25519-dalek/issues/521](https://github.com/dalek-crypt

Curve arithmetic is implemented and used by selecting one of the following backends:

| Backend | Implementation | Target backends |
| :--- | :--- | :--- |
| `[default]` | Serial formulas | `u32` <br/> `u64` |
| `simd` | [Parallel][parallel_doc], using Advanced Vector Extensions | `avx2` <br/> `avx512ifma` |
| `fiat` | Formally verified field arithmetic from [fiat-crypto] | `fiat_u32` <br/> `fiat_u64` |
| Backend | Implementation | Target backends |
| :--- | :--- | :--- |
| `[default]` | Automatic runtime backend selection (either serial or SIMD) | `u32` <br/> `u64` <br/> `avx2` <br/> `avx512` |
| `fiat` | Formally verified field arithmetic from [fiat-crypto] | `fiat_u32` <br/> `fiat_u64` |

To choose a backend other than the `[default]` serial backend, set the
To choose a backend other than the `[default]` backend, set the
environment variable:
```sh
RUSTFLAGS='--cfg curve25519_dalek_backend="BACKEND"'
```
where `BACKEND` is `simd` or `fiat`. Equivalently, you can write to
where `BACKEND` is `fiat`. Equivalently, you can write to
`~/.cargo/config`:
```toml
[build]
rustflags = ['--cfg=curve25519_dalek_backend="BACKEND"']
```
More info [here](https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags).

The `simd` backend requires extra configuration. See [the SIMD
section](#simd-target-backends).

Note for contributors: The target backends are not entirely independent of each
other. The `simd` backend directly depends on parts of the the `u64` backend to
other. The SIMD backend directly depends on parts of the the `u64` backend to
function.

## Word size for serial backends
Expand All @@ -137,7 +136,7 @@ RUSTFLAGS='--cfg curve25519_dalek_bits="SIZE"'
where `SIZE` is `32` or `64`. As in the above section, this can also be placed
in `~/.cargo/config`.

**NOTE:** The `simd` backend CANNOT be used with word size 32.
**NOTE:** Using a word size of 32 will automatically disable SIMD support.

### Cross-compilation

Expand All @@ -152,18 +151,19 @@ $ cargo build --target i686-unknown-linux-gnu

## SIMD target backends

Target backend selection within `simd` must be done manually by setting the
`RUSTFLAGS` environment variable to one of the below options:
The SIMD target backend selection is done automatically at runtime depending
on the available CPU features, provided the appropriate feature flag is enabled.

| CPU feature | `RUSTFLAGS` | Requires nightly? |
| :--- | :--- | :--- |
| avx2 | `-C target_feature=+avx2` | no |
| avx512ifma | `-C target_feature=+avx512ifma` | yes |
You can also specify an appropriate `-C target_feature` to build a binary
which assumes the required SIMD instructions are always available.

Or you can use `-C target_cpu=native` if you don't know what to set.
| Backend | Feature flag | `RUSTFLAGS` | Requires nightly? |
| :--- | :--- | :--- | :--- |
| avx2 | `simd_avx2` | `-C target_feature=+avx2` | no |
| avx512 | `simd_avx512` | `-C target_feature=+avx512ifma,+avx512vl` | yes |

The AVX512 backend requires Rust nightly. If enabled and when compiled on a non-nightly
compiler it will fall back to using the AVX2 backend.
The AVX512 backend requires Rust nightly. When compiled on a non-nightly
compiler it will always be disabled.

# Documentation

Expand Down Expand Up @@ -243,16 +243,16 @@ The implementation is memory-safe, and contains no significant
`unsafe` code. The SIMD backend uses `unsafe` internally to call SIMD
intrinsics. These are marked `unsafe` only because invoking them on an
inappropriate CPU would cause `SIGILL`, but the entire backend is only
compiled with appropriate `target_feature`s, so this cannot occur.
invoked when the appropriate CPU features are detected at runtime, or
when the whole program is compiled with the appropriate `target_feature`s.

# Performance

Benchmarks are run using [`criterion.rs`][criterion]:

```sh
cargo bench --features "rand_core"
# Uses avx2 or ifma only if compiled for an appropriate target.
export RUSTFLAGS='--cfg curve25519_dalek_backend="simd" -C target_cpu=native'
export RUSTFLAGS='-C target_cpu=native'
cargo +nightly bench --features "rand_core"
```

Expand Down Expand Up @@ -294,7 +294,7 @@ universe's beauty, but also his deep hatred of the Daleks. Rusty destroys the
other Daleks and departs the ship, determined to track down and bring an end
to the Dalek race.*

`curve25519-dalek` is authored by Isis Agora Lovecruft and Henry de Valence.
`curve25519-dalek` is authored by Isis Agora Lovecruft and Henry de Valence.

Portions of this library were originally a port of [Adam Langley's
Golang ed25519 library](https://github.com/agl/ed25519), which was in
Expand Down
7 changes: 7 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ fn main() {
{
println!("cargo:rustc-cfg=nightly");
}

let rustc_version = rustc_version::version().expect("failed to detect rustc version");
if rustc_version.major == 1 && rustc_version.minor <= 64 {
// Old versions of Rust complain when you have an `unsafe fn` and you use `unsafe {}` inside,
// so for those we want to apply the `#[allow(unused_unsafe)]` attribute to get rid of that warning.
println!("cargo:rustc-cfg=allow_unused_unsafe");
}
}

// Deterministic cfg(curve25519_dalek_bits) when this is not explicitly set.
Expand Down
Loading

0 comments on commit e111b5d

Please sign in to comment.