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

Performance issue for guest function: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS <= self.max_available_rows #118

Open
medvedNick opened this issue May 26, 2023 · 5 comments

Comments

@medvedNick
Copy link

medvedNick commented May 26, 2023

I'm not able to run pretty simple fibonacci guest function in zkWasm with reasonable performance, meaning I won't be able to run my custom tough app on it.

I've tried to launch that fibonacci program on zkWasm for n > 10000 and it always ends in assertion failed: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS as usize <= self.max_available_rows). I've tried to increase k from default 18 up to 24 and more, and got the same error. For k=25 setup run for more than 20mins, btw.

I understand that wasm is a pretty complex case to zk-verify, but failing calculate fibonacci program means for me no production code could be run on zkWasm. Moreover, other zk-sokutions (such as risk-0) show much better performance. For instance, I'm able to generate proof for n=150000 in 2 mins and verify it for 5ms, compared to my best result with zkWasm with n=1000 with proof generation in 1.5 mins and verification in 170ms.

The question is - is there any way to run the full setup-proof-verify cycle for such a large n like 10000? Is there any parameter other than k which I can change?

Here is the guest function
extern "C" { 
    fn wasm_input(n: i32) -> i64;
    fn log_foreign(value: i64);
}

#[no_mangle]
pub extern "C" fn entrypoint() {
    let input: i32 = unsafe { wasm_input(1) } as i32;
    let f = fibonacci(input);
    unsafe { log_foreign(f as i64) };
}

pub fn fibonacci(n: i32) -> i32 {
    let (mut a, mut b) = (0, 1);
    for _ in 0..n {
        let c = a;
        a = b;
        b += c;
    }
    a
}

log_foreign is just a renamed method log added in register_log_foreign.

I've used methods same as cli uses and my host looks like this
fn main() {
    let n = 10000;
    let wasm_binary = fs::read("res/fib.wasm").expect("No wasm");
    let prefix = "zkwasm";
    let zkwasm_k = 24;
    let function_name = "entrypoint";
    let output_dir: PathBuf = "zk".into();

    let public_inputs: Vec<u64> = vec![n];
    let private_inputs: Vec<u64> = vec![];

    let proof_path: PathBuf = "zk/zkwasm.0.transcript.data".into();

    /////////// setup
    let start = std::time::Instant::now();

    let circuit = build_circuit_without_witness(&wasm_binary, function_name);

    let params = {
        let params_path = &output_dir.join(format!("K{}.params", zkwasm_k));
        load_or_build_unsafe_params::<Bn256>(zkwasm_k, Some(params_path))
    };
    {
        let vk_path = &output_dir.join(format!("{}.{}.vkey.data", prefix, 0));
        load_or_build_vkey::<Bn256, _>(&params, &circuit, Some(vk_path))
    };
    println!("setup time: {:?}", start.elapsed());


    /////////// proof
    let start = std::time::Instant::now();

    let circuit =
        build_circuit_with_witness(&wasm_binary, function_name, &public_inputs, &private_inputs).expect("=== Something went wrong");
    let mut public_inputs_fri = public_inputs
        .iter()
        .map(|v| Fr::from(*v))
        .collect::<Vec<_>>()
        .clone();

    let mut instances = vec![];
    instances.append(&mut public_inputs_fri);

    circuit.tables.write_json(Some(output_dir.clone()));

    let vkey = load_vkey::<Bn256, TestCircuit<_>>(
        &params,
        &output_dir.join(format!("{}.{}.vkey.data", prefix, 0)),
    );

    load_or_create_proof::<Bn256, _>(
        &params,
        vkey,
        circuit.clone(),
        &[&instances],
        Some(&output_dir.join(format!("{}.{}.transcript.data", prefix, 0))),
        TranscriptHash::Poseidon,
        false,
    );
    println!("proof time: {:?}", start.elapsed());


    /////////// dry run
    build_circuit_with_witness(&wasm_binary, function_name, &public_inputs, &private_inputs);

    /////////// verify
    let start = std::time::Instant::now();

    let public_inputs_size = public_inputs.len() + 1;
    let mut public_inputs_fri = public_inputs
        .iter()
        .map(|v| Fr::from(*v))
        .collect::<Vec<_>>()
        .clone();

    let instances = {
        let mut instances = vec![];
        instances.append(&mut public_inputs_fri);
        instances
    };
    let vkey = load_vkey::<Bn256, TestCircuit<_>>(
        &params,
        &output_dir.join(format!("{}.{}.vkey.data", prefix, 0)),
    );

    let proof = load_proof(&proof_path);
    let params_verifier: ParamsVerifier<Bn256> = params.verifier(public_inputs_size).unwrap();
    let strategy = SingleVerifier::new(&params_verifier);

    verify_proof(
        &params_verifier,
        &vkey,
        strategy,
        &[&[&instances]],
        &mut PoseidonRead::init(&proof[..]),
    )
    .expect("Something is incorrect noooooooo");

    println!("verify time: {:?}", start.elapsed());
}

I tried using both main and latest v2 branches of zkWasm with default features. I'm running these benchmarks on my M1 mac btw.

@medvedNick medvedNick changed the title Performance issue for fibonacci guest function (assertion failed: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS as usize <= self.max_available_rows) Performance issue for fibonacci guest function (assertion failed: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS as usize <= self.max_available_rows) May 26, 2023
@medvedNick medvedNick changed the title Performance issue for fibonacci guest function (assertion failed: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS as usize <= self.max_available_rows) Performance issue for fibonacci guest function (event_table.0.len() * EVENT_TABLE_ENTRY_ROWS as usize <= self.max_available_rows) May 26, 2023
@medvedNick medvedNick changed the title Performance issue for fibonacci guest function (event_table.0.len() * EVENT_TABLE_ENTRY_ROWS as usize <= self.max_available_rows) Performance issue for fibonacci guest function: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS <= self.max_available_rows May 26, 2023
@medvedNick medvedNick changed the title Performance issue for fibonacci guest function: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS <= self.max_available_rows Performance issue for guest function: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS <= self.max_available_rows May 26, 2023
@medvedNick medvedNick changed the title Performance issue for guest function: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS <= self.max_available_rows Performance issue for guest function: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS <= self.max_available_rows May 26, 2023
@xgaozoyoe
Copy link
Contributor

You can specify k in cli in zkWASM
For example:
~/zkWasm/target/releae/cli -k 18 ....
Make sure that you use the same k for setup, proof, and batch.

@medvedNick
Copy link
Author

@xgaozoyoe thats exactly what I was doing. As I've mentioned, increasing k from 18 to 24 did nothing, I get the same error anyway. If you look at my code closely, you'll notice that it is exactly the same as in cli

@xgaozoyoe
Copy link
Contributor

I am not sure how you conclude that your code follows exactly the cli source code. My notice is that in cli we use the following:

let zkwasm_k = Self::parse_zkwasm_k_arg(&top_matches).unwrap_or(MIN_K);
set_zkwasm_k(zkwasm_k);

while set_zkwasm_k is implemented as following:
pub const MIN_K: u32 = 18;

lazy_static! {
static ref ZKWASM_K: Mutex =
Mutex::new(env::var("ZKWASM_K").map_or(MIN_K, |k| k.parse().unwrap()));
pub(super) static ref ZKWASM_TABLE_DENOMINATOR: u32 =
env::var("ZKWASM_TABLE_DENOMINATOR").map_or(32, |k| k.parse().unwrap());
static ref ZKWASM_ETABLE_RATIO: u32 =
env::var("ZKWASM_ETABLE_RATIO").map_or(31, |k| k.parse().unwrap());
static ref ZKWASM_MTABLE_RATIO: u32 =
env::var("ZKWASM_MTABLE_RATIO").map_or(31, |k| k.parse().unwrap());
static ref ZKWASM_JTABLE_RATIO: u32 =
env::var("ZKWASM_JTABLE_RATIO").map_or(31, |k| k.parse().unwrap());
static ref ZKWASM_BIT_TABLE_RATIO: u32 =
env::var("ZKWASM_BIT_TABLE_RATIO").map_or(31, |k| k.parse().unwrap());
pub(super) static ref ZKWASM_FOREIGN_CALL_TABLE_RATIO: u32 =
env::var("ZKWASM_FOREIGN_CALL_TABLE_RATIO").map_or(31, |k| k.parse().unwrap());
static ref ZKWASM_SHA256_RATIO: u32 =
env::var("ZKWASM_SHA256_RATIO").map_or(31, |k| k.parse().unwrap());
}

**_pub fn set_zkwasm_k(k: u32) {
assert!(k >= MIN_K);

let mut zkwasm_k = (*ZKWASM_K).lock().unwrap();
*zkwasm_k = k;

}_**

pub fn zkwasm_k() -> u32 {
*ZKWASM_K.lock().unwrap()
}

@xgaozoyoe
Copy link
Contributor

xgaozoyoe commented May 29, 2023

But anyway, thank you very much for trying the codebase. Hope the above comment can help. If you can improve the way of setting k in the codebase, it will be great to have a patch and I am willing to apply it.

@xgaozoyoe
Copy link
Contributor

You can also submit wasm image on https://zkwasm-explorer.delphinuslab.com/ for reporting performance issues with certain wasm image.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants