Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: wasm support and tests #32

Closed
wants to merge 1 commit into from
Closed

feat: wasm support and tests #32

wants to merge 1 commit into from

Conversation

On0n0k1
Copy link

@On0n0k1 On0n0k1 commented Mar 26, 2024

Here is the bug fix for webassembly support to tfhe and concrete-fft for server key deserialization.

To run the tests, all that is needed is to have wasm-bindgen-cli installed and run cargo test --release --target wasm32-unknown-unknown. This link shows how to setup ci-cd for it: https://rustwasm.github.io/wasm-bindgen/wasm-bindgen-test/continuous-integration.html

Here is a description of the changes:

  • .cargo/config tells cargo to use wasm-bindgen runners for the test;
  • Random number generation is not supported by default in wasm. Therefore, ignore compilation of tests that use rug or rand to avoid a panic;
  • wasm is unable to get the current time by default, so std::time::Instant does not compile. Implemented a custom Instant type that will use the type Date to retrieve the current time.
  • The precision of time retrieved by instant is in milliseconds. Because of that, had to change the verification with MIN_DURATION was changed from < to <=. Without this change, this conditional would always be false;
  • With the above changes, the tests will compile. But they will be running in an infinite loop. This is because of the function measure_n_runs. Currently there seems to be a bug with the optimization that causes the variable n_runs to be considered an irrelevant constant that can be optimized. Without this variable, the loop that calls this function will keep running infinitely. Forcing the function to be never inline fix this issue. Try this yourself. Comment that single inline instruction and run the test, the functions will go back to an infinite loop again.
  • There are two tests for the default NodeJs and Browser runtimes. This is enough to prove that the "infinite loop" exists and is fixed by these changes. But I believe you may be interested in adding or change more tests.

Please let me know if there is any issue with these changes. I tested locally with the crate tfhe, and it is able to generate and do computations with the server key properly.

Signed-off-by: Lucas Alessandro do Carmo Lemos <[email protected]>
@IceTDrinker
Copy link
Member

thanks @On0n0k1 we may not have time to review this right away, but I see you manage the dependencies more precisely now

@IceTDrinker
Copy link
Member

hey @On0n0k1 do you have a way to add a targer in the Makefile to run the wasm tests perhaps ?

Or how are we supposed to run the tests ? :)

@On0n0k1
Copy link
Author

On0n0k1 commented Apr 2, 2024

Hello @IceTDrinker . To run the tests, there are two ways: a simple and another more complicated way.

  • The simple way is to just have wasm-pack installed. It will download dependencies and runners according to the requirements of the project. As explained on the link above, just run curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh to install wasm-pack. To test just run wasm-pack test;
  • The more complicated way is to have wasm-bindgen installed. Install wasm-bindgen cli tool, install the target wasm32-unknown-unknown, run cargo test --target wasm32-unknown-unknown. The .cargo/config file tells cargo to automatically use wasm-bindgen to run the tests. By default it will run in a Node-js instance. The browser test has a macro that sets the runner to be a browser instance.

I recommend the "more complicated" way because it is less of a black box. wasm-pack does way too much "magic" for me to trust it.

I can look into the makefile and include the change for the test. Do you have a recommendation or preference on where or how I should make change?

@IceTDrinker
Copy link
Member

Ok for the more complicated one as I agree wasm pack does a lot of magic

somewhere around here

I guess

with maybe a recipe to easily install the right wasm bindgen if required

@IceTDrinker
Copy link
Member

and making sure the test_wasm is in the test_all

@On0n0k1
Copy link
Author

On0n0k1 commented Apr 5, 2024

In what runtime do you use the makefile? One of the issues I've been having deciding is that we need runners for the tests. One of the tests require Node, the other requires a browser like Firefox or Chrome. So we would need these installed in the test environment.

@IceTDrinker
Copy link
Member

that's a good point, we have some infrastructure in the main TFHE crate, we might use some of that here, we'll keep you posted, it is not currently the highest priority on our end, is it something that you actively need ?

@On0n0k1
Copy link
Author

On0n0k1 commented Apr 5, 2024

Yes. With this fix we can use the server key in webassembly, including NodeJs and browser. It opens a lot of options for doing things like "adding homomorphic encryption to a NodeJs instance", "adding homomorphic computations to browser libraries". It will be great for refactoring production systems with the technology.

@IceTDrinker
Copy link
Member

Yes. With this fix we can use the server key in webassembly, including NodeJs and browser. It opens a lot of options for doing things like "adding homomorphic encryption to a NodeJs instance", "adding homomorphic computations to browser libraries". It will be great for refactoring production systems with the technology.

While I understand you want it, be aware that the 32 bits nature of wasm and the fact it is very slow means it won't be usable in practice.

Were you able to test some production-like workflows for testing with this fix ?

@On0n0k1
Copy link
Author

On0n0k1 commented Apr 5, 2024

Yes. Multiplication and division between ciphers is still too slow to be viable. But, by choosing the right configurations, the timing for scalar computations is still good. Would you like me to prepare a benchmark later to show the results?

@IceTDrinker
Copy link
Member

Yes. Multiplication and division between ciphers is still too slow to be viable. But, by choosing the right configurations, the timing for scalar computations is still good. Would you like me to prepare a benchmark later to show the results?

It's not necessary, but if you have some benchmarks with the underlying hardware and the wasm executor we'll take them !

@On0n0k1
Copy link
Author

On0n0k1 commented Apr 5, 2024

I don't have it ready here to show at the moment. Will take some time to prepare. But I've been doing computations in hundreds of integers in the browser with acceptable time for a while (1 to 5 seconds).

What I'm excited about it, is that computation done in the browser is technically "free" cost. The user may get a loading bar as it is waiting the computation to end in a few seconds, but the backend doesn't need to pay for that cost. No cloud service fees with browser computation. Also, when encrypting data, it can be much faster to encrypt the integer 0, then create copies of it, adding the value we need using the server key.

@IceTDrinker
Copy link
Member

Could you maybe describe the use case ? I'm a bit surprised that you want FHE computation in the browser instead of a server 🤔

@On0n0k1
Copy link
Author

On0n0k1 commented Apr 5, 2024

Imagine if you have a network of browsers communicating with one another using Homomorphic encryption. No server. Just several browsers doing the computations securely between one another.

A browser could take the role of the server that way. By arbitrarily doing certain computations on the data without knowing what it is. Imagine the potential that tech like that may have in environments like blockchain...

@IceTDrinker
Copy link
Member

Imagine if you have a network of browsers communicating with one another using Homomorphic encryption. No server. Just several browsers doing the computations securely between one another.

A browser could take the role of the server that way. By arbitrarily doing certain computations on the data without knowing what it is. Imagine the potential that tech like that may have in environments like blockchain...

does that require a browser though, could be a P2P native client ?

Anyways thanks for the insights and what you had in mind

@On0n0k1
Copy link
Author

On0n0k1 commented Apr 8, 2024

How about I remove the browser test and we just keep the NodeJS one? It will still work in the browser the same way. There will be only need to have NodeJs installed in the test machines, which is a lot easier to handle.

@On0n0k1 On0n0k1 closed this Apr 18, 2024
@IceTDrinker
Copy link
Member

How about I remove the browser test and we just keep the NodeJS one? It will still work in the browser the same way. There will be only need to have NodeJs installed in the test machines, which is a lot easier to handle.

Should be ok, though jest with puppeteer can manage browser tests, that’s what we have in TFHE-rs when needing actual browser tests

and sorry for the late reply we have had a busy release @On0n0k1

@IceTDrinker
Copy link
Member

@On0n0k1 any reason you closed the PR ? As I said we have been busy lately we are not against improving platform support but as it's not the priority on our end we dedicate the time we can to it

@On0n0k1
Copy link
Author

On0n0k1 commented Apr 19, 2024

@IceTDrinker Sorry I thought you didn't consider the update necessary. I was thinking how I could convince you to include the fix. So I was thinking about implementing a browser benchmark of tfhe-rs doing the computations with the server key in wasm. A web page that displays the speed of the computations as a table.

I closed it because I would reimplement the changes, maybe by also handling the issue #22. I'm not sure. I don't have much time to work on this either. So if there is anything else I should include in the changes, besides what we already discussed, please let me know.

@On0n0k1 On0n0k1 reopened this Apr 19, 2024
@IceTDrinker
Copy link
Member

so issue 22 is more on our end of things, let's keep this PR open, don't hesitate to bump this when you potentially need it more or have some way to update testing, as already indicated we have some wasm tests in the zama-ai/tfhe-rs repo so with the change you proposed we may be able to provide the functionnality and have tests for it

Note however that it's not clear that the code will properly work for wasm32 as TFHE-rs is designed for 64 bits systems, even with an fft update it might not work, just keeping expectations reasonable here

@IceTDrinker
Copy link
Member

we currently have very limited bandwidth for things we don't have on the roadmap, so don't expect automatic progress on our end for now :)

@IceTDrinker
Copy link
Member

hey, still no bandwidth on our end for now, sorry about that

@@ -0,0 +1,23 @@
#[cfg(target_family = "wasm")]
Copy link
Member

Choose a reason for hiding this comment

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

the cfg can be put in src/time/mod.rs over the mod wasm instead of having it in this file

start: f64,
}

#[cfg(target_family = "wasm")]
Copy link
Member

Choose a reason for hiding this comment

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

same here

@@ -1057,7 +1057,7 @@ fn bit_rev_twice_inv(nbits: u32, base_nbits: u32, i: usize) -> usize {
bit_rev(nbits, i_rev)
}

#[cfg(test)]
#[cfg(all(test, not(target_arch = "wasm32")))]
Copy link
Member

Choose a reason for hiding this comment

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

could you remind us what the issue is for tests on wasm ?

Copy link
Author

Choose a reason for hiding this comment

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

One of the libraries used for generating random values for the tests breaks on my linux machine. I had found the exact dependency that caused it. It was such a long time ago that I forgot which was it. Those tests will run when I test for wasm. Meaning it will start a library that will break on my machine every time. My guess is that it thinks that my Linux machine is a mac.

This is why I had to disable those tests. I apologize for the issue caused by my hardware.

Since those tests couldn't be run in this machine, I can't guarantee that they will run well in your systems compiled to wasm. I will leave that to you.

Making these changes allowed a webpage to generate a server key and do computations on encrypted data. It may be a bit slower, but it didn't seem impactful from the user's perspective.

@IceTDrinker
Copy link
Member

ever managed to discuss with some wasm folks about the infinite loop ? @On0n0k1

@@ -119,6 +119,7 @@ macro_rules! izip {

mod fft_simd;
mod nat;
pub mod time;

Choose a reason for hiding this comment

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

i don't think this should be pub

Copy link
Author

Choose a reason for hiding this comment

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

I agree.

@On0n0k1
Copy link
Author

On0n0k1 commented Jun 25, 2024

ever managed to discuss with some wasm folks about the infinite loop ? @On0n0k1

I found the exact reason why it was running in an infinite loop a few months ago but I forgot by now. I could check it again then explain directly later. It could be a small video or a meeting if necessary.

edit: See below.

@@ -59,6 +59,7 @@ pub enum Method {
}

#[cfg(feature = "std")]
#[cfg_attr(target_family = "wasm", inline(never))]
fn measure_n_runs(
Copy link
Author

Choose a reason for hiding this comment

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

This function is what caused the infinite loop. By including the inline(never), the infinite loop stops. This is because the compiler is incorrectly optimizing this function. It assumes that n_runs is a constant. Therefore it will "optimize" (delete) the code that makes the loop work properly. It will be forever running the same instruction over and over again.

Copy link
Member

Choose a reason for hiding this comment

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

I understand that the optimizer is wrong here so something is likely broken somewhere, as some of those functions at least the time measurement has a side effect and so should not be skipped, so this bug should be reported somewhere if we manage to get a minimal repro

Copy link
Author

Choose a reason for hiding this comment

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

I agree. While I can assure about how the bug happened. I'm still not sure how to replicate it yet. Basically I was logging variables when it was running and suddenly it was working when I logged the variable n_runs.

Once we managed to replicate it, maybe we should report it to the wasm32-unknown-unknown compiler community? Since it only happens in wasm.

Copy link
Member

Choose a reason for hiding this comment

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

I understand but yes once we have something that looks like it reproes the issue we should find a way to share it with the rust project, worst case they can dispatch it themselves afterwards

@@ -59,6 +59,7 @@ pub enum Method {
}

#[cfg(feature = "std")]
#[cfg_attr(target_family = "wasm", inline(never))]
fn measure_n_runs(
n_runs: u128,
Copy link
Author

Choose a reason for hiding this comment

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

Another way to stop the infinite loop from happening. Just call a console.log inside here printing the value of n_runs. The compiler will be unable to optimize the function. Resulting in it working properly.

Copy link
Member

Choose a reason for hiding this comment

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

inline never is good enough I would think, but it needs a comment as to why it is required would be welcome

@On0n0k1 On0n0k1 closed this Jul 23, 2024
@IceTDrinker IceTDrinker mentioned this pull request Sep 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants