From 8c02bb98214cbaf6d01c384b3be1d7979024512e Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 5 Nov 2024 12:43:19 +0000 Subject: [PATCH 1/3] feat: optimize brillig execution of `split_X_bits` functions --- src/utils/split_bits.nr | 71 ++++++++++------------------------------- 1 file changed, 17 insertions(+), 54 deletions(-) diff --git a/src/utils/split_bits.nr b/src/utils/split_bits.nr index 94d79ea9..eafb35d8 100644 --- a/src/utils/split_bits.nr +++ b/src/utils/split_bits.nr @@ -1,38 +1,18 @@ -// Decomposes a single field into two 120 bit fields and a carry -pub unconstrained fn split_120_bits(x: Field) -> (Field, Field) { - let x_bytes: [u8; 32] = x.to_le_bytes(); +global TWO_POW_56: u64 = 0x100000000000000; +global TWO_POW_60: u64 = 0x1000000000000000; +global TWO_POW_64: Field = 0x10000000000000000; - let mut low: Field = 0; - let mut high: Field = 0; +// Decomposes a single field into two 120 bit fields +pub unconstrained fn split_120_bits(mut x: Field) -> (Field, Field) { + // Here's we're taking advantage of truncating 64 bit limbs from the input field + // and then subtracting them from the input such the field division is equivalent to integer division. + let low_lower_64 = (x as u64) as Field; + x = (x - low_lower_64) / TWO_POW_64 as Field; + let low_upper_56 = ((x as u64) % TWO_POW_56) as Field; - let offsets: [Field; 17] = [ - 1, - 0x100, - 0x10000, - 0x1000000, - 0x100000000, - 0x10000000000, - 0x1000000000000, - 0x100000000000000, - 0x10000000000000000, - 0x1000000000000000000, - 0x100000000000000000000, - 0x10000000000000000000000, - 0x1000000000000000000000000, - 0x100000000000000000000000000, - 0x10000000000000000000000000000, - 0x1000000000000000000000000000000, - 0x100000000000000000000000000000000, - ]; + let low = low_lower_64 + TWO_POW_64 * low_upper_56; + let high = (x - low_upper_56) / TWO_POW_56 as Field; - for i in 0..15 { - low += (x_bytes[i] as Field) * offsets[i]; - high += (x_bytes[i + 15] as Field) * offsets[i]; - } - high += (x_bytes[30] as Field) * offsets[15]; - high += (x_bytes[31] as Field) * offsets[16]; - // TDOO: investigate why this is triggered in BigCurve crate? it shouldn't be? - // assert(x_bytes[31] == 0); (low, high) } @@ -42,29 +22,12 @@ pub unconstrained fn split_120_bits(x: Field) -> (Field, Field) { * @description Expects the input limb to be in the range [0, ..., 2^{120 - 1}] **/ pub unconstrained fn split_60_bits(x: Field) -> (u64, u64) { - let x_bytes: [u8; 32] = x.to_le_bytes(); - - let mut low: u64 = 0; - let mut high: u64 = 0; + // Here's we're taking advantage of truncating 64 bit limbs from the input field + // and then subtracting them from the input such the field division is equivalent to integer division. + let x_lower_64 = (x as u64); + let low = x_lower_64 % TWO_POW_60; + let high = ((x - (low as Field)) / TWO_POW_60 as Field) as u64; - let offsets: [u64; 8] = [ - 1, - 0x100, - 0x10000, - 0x1000000, - 0x100000000, - 0x10000000000, - 0x1000000000000, - 0x100000000000000, - ]; - for i in 0..8 { - low += (x_bytes[i] as u64) * offsets[i]; - high += (x_bytes[i + 8] as u64) * offsets[i]; - } - let t1 = low >> 60; - let mask = ((1 as u64) << 60 as u8) - 1; - low = low & mask; - high = (high << 4) | t1; (low, high) } From ceb097bf942b102242e335764a14b19166b42fd1 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 5 Nov 2024 12:52:23 +0000 Subject: [PATCH 2/3] . --- src/runtime_bignum.nr | 2 +- src/utils/split_bits.nr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime_bignum.nr b/src/runtime_bignum.nr index c66bf2a2..3e64566b 100644 --- a/src/runtime_bignum.nr +++ b/src/runtime_bignum.nr @@ -1,5 +1,5 @@ -use crate::utils::map::map; use crate::params::BigNumParams; +use crate::utils::map::map; use crate::fns::{ constrained_ops::{ diff --git a/src/utils/split_bits.nr b/src/utils/split_bits.nr index eafb35d8..4377f0bb 100644 --- a/src/utils/split_bits.nr +++ b/src/utils/split_bits.nr @@ -11,7 +11,7 @@ pub unconstrained fn split_120_bits(mut x: Field) -> (Field, Field) { let low_upper_56 = ((x as u64) % TWO_POW_56) as Field; let low = low_lower_64 + TWO_POW_64 * low_upper_56; - let high = (x - low_upper_56) / TWO_POW_56 as Field; + let high = (x - low_upper_56) / TWO_POW_56 as Field; (low, high) } From 5b654a0ec5622374ccadc1f32367757be9b1e79b Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:12:56 +0000 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Maxim Vezenov --- src/utils/split_bits.nr | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/split_bits.nr b/src/utils/split_bits.nr index 4377f0bb..7a48dbd7 100644 --- a/src/utils/split_bits.nr +++ b/src/utils/split_bits.nr @@ -4,10 +4,10 @@ global TWO_POW_64: Field = 0x10000000000000000; // Decomposes a single field into two 120 bit fields pub unconstrained fn split_120_bits(mut x: Field) -> (Field, Field) { - // Here's we're taking advantage of truncating 64 bit limbs from the input field - // and then subtracting them from the input such the field division is equivalent to integer division. + // Here we're taking advantage of truncating 64 bit limbs from the input field + // and then subtracting them from the input such that the field division is equivalent to integer division. let low_lower_64 = (x as u64) as Field; - x = (x - low_lower_64) / TWO_POW_64 as Field; + x = (x - low_lower_64) / TWO_POW_64; let low_upper_56 = ((x as u64) % TWO_POW_56) as Field; let low = low_lower_64 + TWO_POW_64 * low_upper_56; @@ -22,8 +22,8 @@ pub unconstrained fn split_120_bits(mut x: Field) -> (Field, Field) { * @description Expects the input limb to be in the range [0, ..., 2^{120 - 1}] **/ pub unconstrained fn split_60_bits(x: Field) -> (u64, u64) { - // Here's we're taking advantage of truncating 64 bit limbs from the input field - // and then subtracting them from the input such the field division is equivalent to integer division. + // Here we're taking advantage of truncating 64 bit limbs from the input field + // and then subtracting them from the input such that the field division is equivalent to integer division. let x_lower_64 = (x as u64); let low = x_lower_64 % TWO_POW_60; let high = ((x - (low as Field)) / TWO_POW_60 as Field) as u64;