Skip to content
This repository has been archived by the owner on Feb 29, 2024. It is now read-only.

rust examples #28

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion docs/icicle/rust-bindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,72 @@
`icicle-bn254` being the curve you wish to use and `icicle-core` and `icicle-cuda-runtime` contain ICICLE utilities and CUDA wrappers.

If you wish to point to a specific ICICLE branch add `branch = "<name_of_branch>"` or `tag = "<name_of_tag>"` to the ICICLE dependency. For a specific commit add `rev = "<commit_id>"`.

Lets write a quick example to demonstrate how you would use the Rust ICICLE library. Create a file named `main.rs` in your project, with the following:
jeremyfelder marked this conversation as resolved.
Show resolved Hide resolved

When you build your project ICICLE will be built as part of the build command.
```rust
use icicle_bn254::curve::{
ScalarCfg,
ScalarField,
};

use icicle_cuda_runtime::{
stream::CudaStream,
memory::DeviceSlice,
device_context::get_default_device_context
};

use icicle_core::{
ntt::{self, NTT},
};

fn main() {
let size = 1 << 23;
let scalars = ScalarCfg::generate_random(size);
let mut ntt_results: DeviceSlice<'_, ScalarField> = DeviceSlice::cuda_malloc(size).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't describe the current API, uses HostOrDeviceSlice

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@jeremyfelder jeremyfelder Jan 29, 2024

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh ok thanks


let icicle_omega = <Bn254Fr as FftField>::get_root_of_unity(size.try_into().unwrap()).unwrap();
let ctx = get_default_device_context();
ScalarCfg::initialize_domain(ScalarField::from_ark(icicle_omega), &ctx).unwrap();

let stream = CudaStream::create().unwrap();
let mut cfg = ntt::get_default_ntt_config::<ScalarField>();
cfg.ctx.stream = &stream;
cfg.is_async = true;
cfg.are_outputs_on_device = true;

ntt::ntt(scalars.as_slice(), ntt::NTTDir::kForward, &cfg, ntt_results.as_slice()).unwrap();

stream.synchronize().unwrap();
let mut host_bn254_results = vec![ScalarField::zero(); size];
ntt_results.copy_to_host(&mut host_bn254_results[..]).unwrap();

stream.destroy().unwrap();
}
```

Using `ScalarCfg::generate_random` we generate random inputs for our `NTT` method, we also initialize the domain `initialize_domain` this generates twiddle factors which will be used to compute NTTs.

We also create a CUDA stream by calling `CudaStream::create().unwrap()`, this stream will be used to send the data over to the GPU and return the result.


By calling `ntt::ntt(scalars.as_slice(), ntt::NTTDir::kForward, &cfg, ntt_results.as_slice()).unwrap();` we send the configurations, scalars and result pointer over to the GPU to preform the NTT computation.

Check failure on line 74 in docs/icicle/rust-bindings.md

View workflow job for this annotation

GitHub Actions / Check Spelling

preform ==> perform

By default CUDA kernels run in parallel to the CPU, so after triggering your `ntt` method if you need to wait for the result you must synchronize your stream. This can be done easily with `stream.synchronize().unwrap();` your CPU thread will now wait for all CUDA kernels to resolve.

To retrieve the result you must copy the result from device to host.

```
...
ntt_results.copy_to_host(&mut host_bn254_results[..]).unwrap();
...
```

Finally to clean up just destroy the stream you have been using.

This example covers the basic usage of the ICICLE Rust bindings.
Comment on lines +69 to +88
Copy link
Contributor

Choose a reason for hiding this comment

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

Its pretty typical for a short example to have the full code and then go line by line explaining whats going on. I know its a lot more to write out but it reads much better and is something anyone would be able to follow


For more examples refer [here](https://github.com/ingonyama-zk/icicle/tree/main/examples).


# How do the rust bindings work?
Expand Down