diff --git a/libff/CMakeLists.txt b/libff/CMakeLists.txt index 4995b754..586571be 100755 --- a/libff/CMakeLists.txt +++ b/libff/CMakeLists.txt @@ -74,6 +74,18 @@ install( TARGETS ff DESTINATION lib ) +add_executable( + multiexp_profile + EXCLUDE_FROM_ALL + + algebra/scalar_multiplication/multiexp_profile.cpp +) +target_link_libraries( + multiexp_profile + + ff +) + # Tests add_executable( algebra_bilinearity_test diff --git a/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp b/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp index cd5bb778..214e96c2 100755 --- a/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp +++ b/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp @@ -496,8 +496,7 @@ std::istream& operator>>(std::istream& in, std::vector &v) return in; } -template<> -void batch_to_special_all_non_zeros(std::vector &vec) +void alt_bn128_G1::batch_to_special_all_non_zeros(std::vector &vec) { std::vector Z_vec; Z_vec.reserve(vec.size()); diff --git a/libff/algebra/curves/alt_bn128/alt_bn128_g1.hpp b/libff/algebra/curves/alt_bn128/alt_bn128_g1.hpp index 46726ac1..bfeed20f 100755 --- a/libff/algebra/curves/alt_bn128/alt_bn128_g1.hpp +++ b/libff/algebra/curves/alt_bn128/alt_bn128_g1.hpp @@ -70,6 +70,8 @@ class alt_bn128_G1 { friend std::ostream& operator<<(std::ostream &out, const alt_bn128_G1 &g); friend std::istream& operator>>(std::istream &in, alt_bn128_G1 &g); + + static void batch_to_special_all_non_zeros(std::vector &vec); }; template @@ -87,10 +89,5 @@ alt_bn128_G1 operator*(const Fp_model &lhs, const alt_bn128_G1 &rhs std::ostream& operator<<(std::ostream& out, const std::vector &v); std::istream& operator>>(std::istream& in, std::vector &v); -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec); - } // libff #endif // ALT_BN128_G1_HPP_ diff --git a/libff/algebra/curves/alt_bn128/alt_bn128_g2.cpp b/libff/algebra/curves/alt_bn128/alt_bn128_g2.cpp index 8887347e..4e8c9944 100755 --- a/libff/algebra/curves/alt_bn128/alt_bn128_g2.cpp +++ b/libff/algebra/curves/alt_bn128/alt_bn128_g2.cpp @@ -477,8 +477,7 @@ std::istream& operator>>(std::istream &in, alt_bn128_G2 &g) return in; } -template<> -void batch_to_special_all_non_zeros(std::vector &vec) +void alt_bn128_G2::batch_to_special_all_non_zeros(std::vector &vec) { std::vector Z_vec; Z_vec.reserve(vec.size()); diff --git a/libff/algebra/curves/alt_bn128/alt_bn128_g2.hpp b/libff/algebra/curves/alt_bn128/alt_bn128_g2.hpp index 54bd6a46..2b628ebf 100755 --- a/libff/algebra/curves/alt_bn128/alt_bn128_g2.hpp +++ b/libff/algebra/curves/alt_bn128/alt_bn128_g2.hpp @@ -74,6 +74,8 @@ class alt_bn128_G2 { friend std::ostream& operator<<(std::ostream &out, const alt_bn128_G2 &g); friend std::istream& operator>>(std::istream &in, alt_bn128_G2 &g); + + static void batch_to_special_all_non_zeros(std::vector &vec); }; template @@ -88,10 +90,6 @@ alt_bn128_G2 operator*(const Fp_model &lhs, const alt_bn128_G2 &rhs return scalar_mul(rhs, lhs.as_bigint()); } -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec); } // libff #endif // ALT_BN128_G2_HPP_ diff --git a/libff/algebra/curves/bn128/bn128_g1.cpp b/libff/algebra/curves/bn128/bn128_g1.cpp index 44685782..b8389e68 100755 --- a/libff/algebra/curves/bn128/bn128_g1.cpp +++ b/libff/algebra/curves/bn128/bn128_g1.cpp @@ -491,8 +491,7 @@ std::istream& operator>>(std::istream& in, std::vector &v) return in; } -template<> -void batch_to_special_all_non_zeros(std::vector &vec) +void bn128_G1::batch_to_special_all_non_zeros(std::vector &vec) { std::vector Z_vec; Z_vec.reserve(vec.size()); diff --git a/libff/algebra/curves/bn128/bn128_g1.hpp b/libff/algebra/curves/bn128/bn128_g1.hpp index 5049b592..fe2c8976 100755 --- a/libff/algebra/curves/bn128/bn128_g1.hpp +++ b/libff/algebra/curves/bn128/bn128_g1.hpp @@ -70,6 +70,8 @@ class bn128_G1 { friend std::ostream& operator<<(std::ostream &out, const bn128_G1 &g); friend std::istream& operator>>(std::istream &in, bn128_G1 &g); + + static void batch_to_special_all_non_zeros(std::vector &vec); }; template @@ -87,10 +89,6 @@ bn128_G1 operator*(const Fp_model &lhs, const bn128_G1 &rhs) std::ostream& operator<<(std::ostream& out, const std::vector &v); std::istream& operator>>(std::istream& in, std::vector &v); -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec); } // libff #endif // BN128_G1_HPP_ diff --git a/libff/algebra/curves/bn128/bn128_g2.cpp b/libff/algebra/curves/bn128/bn128_g2.cpp index 7a6c3476..cd8eb72f 100755 --- a/libff/algebra/curves/bn128/bn128_g2.cpp +++ b/libff/algebra/curves/bn128/bn128_g2.cpp @@ -474,8 +474,7 @@ std::istream& operator>>(std::istream &in, bn128_G2 &g) return in; } -template<> -void batch_to_special_all_non_zeros(std::vector &vec) +void bn128_G2::batch_to_special_all_non_zeros(std::vector &vec) { std::vector Z_vec; Z_vec.reserve(vec.size()); diff --git a/libff/algebra/curves/bn128/bn128_g2.hpp b/libff/algebra/curves/bn128/bn128_g2.hpp index 45e4e5c2..3b5fccc5 100755 --- a/libff/algebra/curves/bn128/bn128_g2.hpp +++ b/libff/algebra/curves/bn128/bn128_g2.hpp @@ -71,6 +71,8 @@ class bn128_G2 { friend std::ostream& operator<<(std::ostream &out, const bn128_G2 &g); friend std::istream& operator>>(std::istream &in, bn128_G2 &g); + + static void batch_to_special_all_non_zeros(std::vector &vec); }; template @@ -85,10 +87,5 @@ bn128_G2 operator*(const Fp_model &lhs, const bn128_G2 &rhs) return scalar_mul(rhs, lhs.as_bigint()); } -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec); - } // libff #endif // BN128_G2_HPP_ diff --git a/libff/algebra/curves/edwards/edwards_g1.cpp b/libff/algebra/curves/edwards/edwards_g1.cpp index 9db382a1..d7749ada 100755 --- a/libff/algebra/curves/edwards/edwards_g1.cpp +++ b/libff/algebra/curves/edwards/edwards_g1.cpp @@ -388,8 +388,7 @@ std::istream& operator>>(std::istream& in, std::vector &v) return in; } -template<> -void batch_to_special_all_non_zeros(std::vector &vec) +void edwards_G1::batch_to_special_all_non_zeros(std::vector &vec) { std::vector Z_vec; Z_vec.reserve(vec.size()); diff --git a/libff/algebra/curves/edwards/edwards_g1.hpp b/libff/algebra/curves/edwards/edwards_g1.hpp index 33ee926c..91cc4d18 100755 --- a/libff/algebra/curves/edwards/edwards_g1.hpp +++ b/libff/algebra/curves/edwards/edwards_g1.hpp @@ -72,6 +72,8 @@ class edwards_G1 { friend std::ostream& operator<<(std::ostream &out, const edwards_G1 &g); friend std::istream& operator>>(std::istream &in, edwards_G1 &g); + + static void batch_to_special_all_non_zeros(std::vector &vec); }; template @@ -89,10 +91,5 @@ edwards_G1 operator*(const Fp_model &lhs, const edwards_G1 &rhs) std::ostream& operator<<(std::ostream& out, const std::vector &v); std::istream& operator>>(std::istream& in, std::vector &v); -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec); - } // libff #endif // EDWARDS_G1_HPP_ diff --git a/libff/algebra/curves/edwards/edwards_g2.cpp b/libff/algebra/curves/edwards/edwards_g2.cpp index 118a23d4..923a5451 100755 --- a/libff/algebra/curves/edwards/edwards_g2.cpp +++ b/libff/algebra/curves/edwards/edwards_g2.cpp @@ -389,10 +389,7 @@ std::istream& operator>>(std::istream &in, edwards_G2 &g) return in; } -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec) +void edwards_G2::batch_to_special_all_non_zeros(std::vector &vec) { std::vector Z_vec; Z_vec.reserve(vec.size()); diff --git a/libff/algebra/curves/edwards/edwards_g2.hpp b/libff/algebra/curves/edwards/edwards_g2.hpp index b6390b0b..09d2319d 100755 --- a/libff/algebra/curves/edwards/edwards_g2.hpp +++ b/libff/algebra/curves/edwards/edwards_g2.hpp @@ -78,6 +78,8 @@ class edwards_G2 { friend std::ostream& operator<<(std::ostream &out, const edwards_G2 &g); friend std::istream& operator>>(std::istream &in, edwards_G2 &g); + + static void batch_to_special_all_non_zeros(std::vector &vec); }; template @@ -92,10 +94,5 @@ edwards_G2 operator*(const Fp_model &lhs, const edwards_G2 &rhs) return scalar_mul(rhs, lhs.as_bigint()); } -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec); - } // libff #endif // EDWARDS_G2_HPP_ diff --git a/libff/algebra/curves/mnt/mnt4/mnt4_g1.cpp b/libff/algebra/curves/mnt/mnt4/mnt4_g1.cpp index d0ef8e0c..ef061caa 100755 --- a/libff/algebra/curves/mnt/mnt4/mnt4_g1.cpp +++ b/libff/algebra/curves/mnt/mnt4/mnt4_g1.cpp @@ -482,8 +482,7 @@ std::istream& operator>>(std::istream& in, std::vector &v) return in; } -template<> -void batch_to_special_all_non_zeros(std::vector &vec) +void mnt4_G1::batch_to_special_all_non_zeros(std::vector &vec) { std::vector Z_vec; Z_vec.reserve(vec.size()); diff --git a/libff/algebra/curves/mnt/mnt4/mnt4_g1.hpp b/libff/algebra/curves/mnt/mnt4/mnt4_g1.hpp index 7dcf9832..a4dfc7e7 100755 --- a/libff/algebra/curves/mnt/mnt4/mnt4_g1.hpp +++ b/libff/algebra/curves/mnt/mnt4/mnt4_g1.hpp @@ -82,6 +82,8 @@ class mnt4_G1 { friend std::ostream& operator<<(std::ostream &out, const mnt4_G1 &g); friend std::istream& operator>>(std::istream &in, mnt4_G1 &g); + + static void batch_to_special_all_non_zeros(std::vector &vec); }; template @@ -99,11 +101,6 @@ mnt4_G1 operator*(const Fp_model &lhs, const mnt4_G1 &rhs) std::ostream& operator<<(std::ostream& out, const std::vector &v); std::istream& operator>>(std::istream& in, std::vector &v); -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec); - } // libff #endif // MNT4_G1_HPP_ diff --git a/libff/algebra/curves/mnt/mnt4/mnt4_g2.cpp b/libff/algebra/curves/mnt/mnt4/mnt4_g2.cpp index 4d3856e2..757bba37 100755 --- a/libff/algebra/curves/mnt/mnt4/mnt4_g2.cpp +++ b/libff/algebra/curves/mnt/mnt4/mnt4_g2.cpp @@ -473,8 +473,7 @@ std::istream& operator>>(std::istream &in, mnt4_G2 &g) return in; } -template<> -void batch_to_special_all_non_zeros(std::vector &vec) +void mnt4_G2::batch_to_special_all_non_zeros(std::vector &vec) { std::vector Z_vec; Z_vec.reserve(vec.size()); diff --git a/libff/algebra/curves/mnt/mnt4/mnt4_g2.hpp b/libff/algebra/curves/mnt/mnt4/mnt4_g2.hpp index fb794e34..9d403214 100755 --- a/libff/algebra/curves/mnt/mnt4/mnt4_g2.hpp +++ b/libff/algebra/curves/mnt/mnt4/mnt4_g2.hpp @@ -87,6 +87,8 @@ class mnt4_G2 { friend std::ostream& operator<<(std::ostream &out, const mnt4_G2 &g); friend std::istream& operator>>(std::istream &in, mnt4_G2 &g); + + static void batch_to_special_all_non_zeros(std::vector &vec); }; template @@ -101,11 +103,6 @@ mnt4_G2 operator*(const Fp_model &lhs, const mnt4_G2 &rhs) return scalar_mul(rhs, lhs.as_bigint()); } -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec); - } // libff #endif // MNT4_G2_HPP_ diff --git a/libff/algebra/curves/mnt/mnt6/mnt6_g1.cpp b/libff/algebra/curves/mnt/mnt6/mnt6_g1.cpp index 3c719786..aea73e4d 100755 --- a/libff/algebra/curves/mnt/mnt6/mnt6_g1.cpp +++ b/libff/algebra/curves/mnt/mnt6/mnt6_g1.cpp @@ -481,8 +481,7 @@ std::istream& operator>>(std::istream& in, std::vector &v) return in; } -template<> -void batch_to_special_all_non_zeros(std::vector &vec) +void mnt6_G1::batch_to_special_all_non_zeros(std::vector &vec) { std::vector Z_vec; Z_vec.reserve(vec.size()); diff --git a/libff/algebra/curves/mnt/mnt6/mnt6_g1.hpp b/libff/algebra/curves/mnt/mnt6/mnt6_g1.hpp index 59ec8755..a2a08ea7 100755 --- a/libff/algebra/curves/mnt/mnt6/mnt6_g1.hpp +++ b/libff/algebra/curves/mnt/mnt6/mnt6_g1.hpp @@ -82,6 +82,8 @@ class mnt6_G1 { friend std::ostream& operator<<(std::ostream &out, const mnt6_G1 &g); friend std::istream& operator>>(std::istream &in, mnt6_G1 &g); + + static void batch_to_special_all_non_zeros(std::vector &vec); }; template @@ -99,11 +101,6 @@ mnt6_G1 operator*(const Fp_model &lhs, const mnt6_G1 &rhs) std::ostream& operator<<(std::ostream& out, const std::vector &v); std::istream& operator>>(std::istream& in, std::vector &v); -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec); - } // libff #endif // MNT6_G1_HPP_ diff --git a/libff/algebra/curves/mnt/mnt6/mnt6_g2.cpp b/libff/algebra/curves/mnt/mnt6/mnt6_g2.cpp index 76103e50..1bcc4dd3 100755 --- a/libff/algebra/curves/mnt/mnt6/mnt6_g2.cpp +++ b/libff/algebra/curves/mnt/mnt6/mnt6_g2.cpp @@ -480,8 +480,7 @@ std::istream& operator>>(std::istream &in, mnt6_G2 &g) return in; } -template<> -void batch_to_special_all_non_zeros(std::vector &vec) +void mnt6_G2::batch_to_special_all_non_zeros(std::vector &vec) { std::vector Z_vec; Z_vec.reserve(vec.size()); diff --git a/libff/algebra/curves/mnt/mnt6/mnt6_g2.hpp b/libff/algebra/curves/mnt/mnt6/mnt6_g2.hpp index 80053193..2dc11e34 100755 --- a/libff/algebra/curves/mnt/mnt6/mnt6_g2.hpp +++ b/libff/algebra/curves/mnt/mnt6/mnt6_g2.hpp @@ -87,6 +87,8 @@ class mnt6_G2 { friend std::ostream& operator<<(std::ostream &out, const mnt6_G2 &g); friend std::istream& operator>>(std::istream &in, mnt6_G2 &g); + + static void batch_to_special_all_non_zeros(std::vector &vec); }; template @@ -101,11 +103,6 @@ mnt6_G2 operator*(const Fp_model &lhs, const mnt6_G2 &rhs) return scalar_mul(rhs, lhs.as_bigint()); } -template -void batch_to_special_all_non_zeros(std::vector &vec); -template<> -void batch_to_special_all_non_zeros(std::vector &vec); - } // libff #endif // MNT6_G2_HPP_ diff --git a/libff/algebra/scalar_multiplication/multiexp.hpp b/libff/algebra/scalar_multiplication/multiexp.hpp index 1838f20e..bbbdb040 100755 --- a/libff/algebra/scalar_multiplication/multiexp.hpp +++ b/libff/algebra/scalar_multiplication/multiexp.hpp @@ -12,52 +12,77 @@ #ifndef MULTIEXP_HPP_ #define MULTIEXP_HPP_ +#include #include namespace libff { -/** - * Naive multi-exponentiation individually multiplies each base by the - * corresponding scalar and adds up the results. - */ -template -T naive_exp(typename std::vector::const_iterator vec_start, - typename std::vector::const_iterator vec_end, - typename std::vector::const_iterator scalar_start, - typename std::vector::const_iterator scalar_end); - -template -T naive_plain_exp(typename std::vector::const_iterator vec_start, - typename std::vector::const_iterator vec_end, - typename std::vector::const_iterator scalar_start, - typename std::vector::const_iterator scalar_end); +enum multi_exp_method { + /** + * Naive multi-exponentiation individually multiplies each base by the + * corresponding scalar and adds up the results. + * multi_exp_method_naive uses opt_window_wnaf_exp for exponentiation, + * while multi_exp_method_plain uses operator *. + */ + multi_exp_method_naive, + multi_exp_method_naive_plain, + /** + * A variant of the Bos-Coster algorithm [1], + * with implementation suggestions from [2]. + * + * [1] = Bos and Coster, "Addition chain heuristics", CRYPTO '89 + * [2] = Bernstein, Duif, Lange, Schwabe, and Yang, "High-speed high-security signatures", CHES '11 + */ + multi_exp_method_bos_coster, + /** + * A special case of Pippenger's algorithm from Page 15 of + * Bernstein, Doumen, Lange, Oosterwijk, + * "Faster batch forgery identification", INDOCRYPT 2012 + * (https://eprint.iacr.org/2012/549.pdf) + * When compiled with USE_MIXED_ADDITION, assumes input is in special form. + * Requires that T implements .dbl() (and, if USE_MIXED_ADDITION is defined, + * .to_special(), .mixed_add(), and batch_to_special()). + */ + multi_exp_method_BDLO12 +}; /** - * Naive multi-exponentiation uses a variant of the Bos-Coster algorithm [1], - * and implementation suggestions from [2]. - * - * [1] = Bos and Coster, "Addition chain heuristics", CRYPTO '89 - * [2] = Bernstein, Duif, Lange, Schwabe, and Yang, "High-speed high-security signatures", CHES '11 + * Computes the sum + * \sum_i scalar_start[i] * vec_start[i] + * using the selected method. + * Input is split into the given number of chunks, and, when compiled with + * MULTICORE, the chunks are processed in parallel. */ -template +template T multi_exp(typename std::vector::const_iterator vec_start, typename std::vector::const_iterator vec_end, typename std::vector::const_iterator scalar_start, typename std::vector::const_iterator scalar_end, - const size_t chunks, - const bool use_multiexp=false); + const size_t chunks); /** - * A variant of multi_exp that takes advantage of the method mixed_add (instead of the operator '+'). + * A variant of multi_exp that takes advantage of the method mixed_add (instead + * of the operator '+'). + * Assumes input is in special form, and includes special pre-processing for + * scalars equal to 0 or 1. */ -template +template T multi_exp_with_mixed_addition(typename std::vector::const_iterator vec_start, - typename std::vector::const_iterator vec_end, - typename std::vector::const_iterator scalar_start, - typename std::vector::const_iterator scalar_end, - const size_t chunks, - const bool use_multiexp); + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end, + const size_t chunks); + +/** + * A convenience function for calculating a pure inner product, where the + * more complicated methods are not required. + */ +template +T inner_product(typename std::vector::const_iterator a_start, + typename std::vector::const_iterator a_end, + typename std::vector::const_iterator b_start, + typename std::vector::const_iterator b_end); /** * A window table stores window sizes for different instance sizes for fixed-base multi-scalar multiplications. @@ -98,10 +123,6 @@ std::vector batch_exp_with_coeff(const size_t scalar_size, const FieldT &coeff, const std::vector &v); -// defined in every curve -template -void batch_to_special_all_non_zeros(std::vector &vec); - template void batch_to_special(std::vector &vec); diff --git a/libff/algebra/scalar_multiplication/multiexp.tcc b/libff/algebra/scalar_multiplication/multiexp.tcc index 9ffc4422..69eb184d 100755 --- a/libff/algebra/scalar_multiplication/multiexp.tcc +++ b/libff/algebra/scalar_multiplication/multiexp.tcc @@ -18,7 +18,9 @@ #include #include +#include #include +#include #include #include #include @@ -102,11 +104,26 @@ public: } }; -template -T naive_exp(typename std::vector::const_iterator vec_start, - typename std::vector::const_iterator vec_end, - typename std::vector::const_iterator scalar_start, - typename std::vector::const_iterator scalar_end) +/** + * multi_exp_inner() implementes the specified + * multiexponentiation method. + * this implementation relies on some rather arcane template magic: + * function templates cannot be partially specialized, so we cannot just write + * template + * T multi_exp_inner + * thus we resort to using std::enable_if. the basic idea is that *overloading* + * is what's actually happening here, it's just that, for any given value of + * Method, only one of the templates will be valid, and thus the correct + * implementation will be used. + */ + +template::type = 0> +T multi_exp_inner( + typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end) { T result(T::zero()); @@ -123,11 +140,13 @@ T naive_exp(typename std::vector::const_iterator vec_start, return result; } -template -T naive_plain_exp(typename std::vector::const_iterator vec_start, - typename std::vector::const_iterator vec_end, - typename std::vector::const_iterator scalar_start, - typename std::vector::const_iterator scalar_end) +template::type = 0> +T multi_exp_inner( + typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end) { T result(T::zero()); @@ -143,17 +162,132 @@ T naive_plain_exp(typename std::vector::const_iterator vec_start, return result; } -/* - The multi-exponentiation algorithm below is a variant of the Bos-Coster algorithm - [Bos and Coster, "Addition chain heuristics", CRYPTO '89]. - The implementation uses suggestions from - [Bernstein, Duif, Lange, Schwabe, and Yang, "High-speed high-security signatures", CHES '11]. -*/ -template -T multi_exp_inner(typename std::vector::const_iterator vec_start, - typename std::vector::const_iterator vec_end, - typename std::vector::const_iterator scalar_start, - typename std::vector::const_iterator scalar_end) +template::type = 0> +T multi_exp_inner( + typename std::vector::const_iterator bases, + typename std::vector::const_iterator bases_end, + typename std::vector::const_iterator exponents, + typename std::vector::const_iterator exponents_end) +{ + UNUSED(exponents_end); + size_t length = bases_end - bases; + + // empirically, this seems to be a decent estimate of the optimal value of c + size_t log2_length = log2(length); + size_t c = log2_length - (log2_length / 3 - 2); + + const mp_size_t exp_num_limbs = + std::remove_reference::type::num_limbs; + std::vector > bn_exponents(length); + size_t num_bits = 0; + + for (size_t i = 0; i < length; i++) + { + bn_exponents[i] = exponents[i].as_bigint(); + num_bits = std::max(num_bits, bn_exponents[i].num_bits()); + } + + size_t num_groups = (num_bits + c - 1) / c; + + T result; + bool result_nonzero = false; + + for (size_t k = num_groups - 1; k <= num_groups; k--) + { + if (result_nonzero) + { + for (size_t i = 0; i < c; i++) + { + result = result.dbl(); + } + } + + std::vector buckets(1 << c); + std::vector bucket_nonzero(1 << c); + + for (size_t i = 0; i < length; i++) + { + size_t id = 0; + for (size_t j = 0; j < c; j++) + { + if (bn_exponents[i].test_bit(k*c + j)) + { + id |= 1 << j; + } + } + + if (id == 0) + { + continue; + } + + if (bucket_nonzero[id]) + { +#ifdef USE_MIXED_ADDITION + buckets[id] = buckets[id].mixed_add(bases[i]); +#else + buckets[id] = buckets[id] + bases[i]; +#endif + } + else + { + buckets[id] = bases[i]; + bucket_nonzero[id] = true; + } + } + +#ifdef USE_MIXED_ADDITION + batch_to_special(buckets); +#endif + + T running_sum; + bool running_sum_nonzero = false; + + for (size_t i = (1u << c) - 1; i > 0; i--) + { + if (bucket_nonzero[i]) + { + if (running_sum_nonzero) + { +#ifdef USE_MIXED_ADDITION + running_sum = running_sum.mixed_add(buckets[i]); +#else + running_sum = running_sum + buckets[i]; +#endif + } + else + { + running_sum = buckets[i]; + running_sum_nonzero = true; + } + } + + if (running_sum_nonzero) + { + if (result_nonzero) + { + result = result + running_sum; + } + else + { + result = running_sum; + result_nonzero = true; + } + } + } + } + + return result; +} + +template::type = 0> +T multi_exp_inner( + typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end) { const mp_size_t n = std::remove_reference::type::num_limbs; @@ -265,49 +399,35 @@ T multi_exp_inner(typename std::vector::const_iterator vec_start, return opt_result; } -template +template T multi_exp(typename std::vector::const_iterator vec_start, typename std::vector::const_iterator vec_end, typename std::vector::const_iterator scalar_start, typename std::vector::const_iterator scalar_end, - const size_t chunks, - const bool use_multiexp) + const size_t chunks) { const size_t total = vec_end - vec_start; - if (total < chunks) + if ((total < chunks) || (chunks == 1)) { - return naive_exp(vec_start, vec_end, scalar_start, scalar_end); + // no need to split into "chunks", can call implementation directly + return multi_exp_inner( + vec_start, vec_end, scalar_start, scalar_end); } const size_t one = total/chunks; std::vector partial(chunks, T::zero()); - if (use_multiexp) - { #ifdef MULTICORE #pragma omp parallel for #endif - for (size_t i = 0; i < chunks; ++i) - { - partial[i] = multi_exp_inner(vec_start + i*one, - (i == chunks-1 ? vec_end : vec_start + (i+1)*one), - scalar_start + i*one, - (i == chunks-1 ? scalar_end : scalar_start + (i+1)*one)); - } - } - else + for (size_t i = 0; i < chunks; ++i) { -#ifdef MULTICORE -#pragma omp parallel for -#endif - for (size_t i = 0; i < chunks; ++i) - { - partial[i] = naive_exp(vec_start + i*one, - (i == chunks-1 ? vec_end : vec_start + (i+1)*one), - scalar_start + i*one, - (i == chunks-1 ? scalar_end : scalar_start + (i+1)*one)); - } + partial[i] = multi_exp_inner( + vec_start + i*one, + (i == chunks-1 ? vec_end : vec_start + (i+1)*one), + scalar_start + i*one, + (i == chunks-1 ? scalar_end : scalar_start + (i+1)*one)); } T final = T::zero(); @@ -320,13 +440,12 @@ T multi_exp(typename std::vector::const_iterator vec_start, return final; } -template +template T multi_exp_with_mixed_addition(typename std::vector::const_iterator vec_start, typename std::vector::const_iterator vec_end, typename std::vector::const_iterator scalar_start, typename std::vector::const_iterator scalar_end, - const size_t chunks, - const bool use_multiexp) + const size_t chunks) { assert(std::distance(vec_start, vec_end) == std::distance(scalar_start, scalar_end)); enter_block("Process scalar vector"); @@ -373,7 +492,18 @@ T multi_exp_with_mixed_addition(typename std::vector::const_iterator vec_star leave_block("Process scalar vector"); - return acc + multi_exp(g.begin(), g.end(), p.begin(), p.end(), chunks, use_multiexp); + return acc + multi_exp(g.begin(), g.end(), p.begin(), p.end(), chunks); +} + +template +T inner_product(typename std::vector::const_iterator a_start, + typename std::vector::const_iterator a_end, + typename std::vector::const_iterator b_start, + typename std::vector::const_iterator b_end) +{ + return multi_exp( + a_start, a_end, + b_start, b_end, 1); } template @@ -564,7 +694,7 @@ void batch_to_special(std::vector &vec) } } - batch_to_special_all_non_zeros(non_zero_vec); + T::batch_to_special_all_non_zeros(non_zero_vec); auto it = non_zero_vec.begin(); T zero_special = T::zero(); zero_special.to_special(); diff --git a/libff/algebra/scalar_multiplication/multiexp_profile.cpp b/libff/algebra/scalar_multiplication/multiexp_profile.cpp new file mode 100644 index 00000000..0434bb25 --- /dev/null +++ b/libff/algebra/scalar_multiplication/multiexp_profile.cpp @@ -0,0 +1,128 @@ +#include +#include + +#include +#include +#include +#include + +using namespace libff; + +template +using run_result_t = std::pair >; + +template +using test_instances_t = std::vector >; + +template +test_instances_t generate_group_elements(size_t count, size_t size) +{ + // generating a random group element is expensive, + // so for now we only generate a single one and repeat it + test_instances_t result(count); + + for (size_t i = 0; i < count; i++) { + GroupT x = GroupT::random_element(); + x.to_special(); // djb requires input to be in special form + for (size_t j = 0; j < size; j++) { + result[i].push_back(x); + // result[i].push_back(GroupT::random_element()); + } + } + + return result; +} + +template +test_instances_t generate_scalars(size_t count, size_t size) +{ + // we use SHA512_rng because it is much faster than + // FieldT::random_element() + test_instances_t result(count); + + for (size_t i = 0; i < count; i++) { + for (size_t j = 0; j < size; j++) { + result[i].push_back(SHA512_rng(i * size + j)); + } + } + + return result; +} + +template +run_result_t profile_multiexp( + test_instances_t group_elements, + test_instances_t scalars) +{ + long long start_time = get_nsec_time(); + + std::vector answers; + for (size_t i = 0; i < group_elements.size(); i++) { + answers.push_back(multi_exp( + group_elements[i].cbegin(), group_elements[i].cend(), + scalars[i].cbegin(), scalars[i].cend(), + 1)); + } + + long long time_delta = get_nsec_time() - start_time; + + return run_result_t(time_delta, answers); +} + +template +void print_performance_csv( + size_t expn_start, + size_t expn_end_fast, + size_t expn_end_naive, + bool compare_answers) +{ + for (size_t expn = expn_start; expn <= expn_end_fast; expn++) { + printf("%ld", expn); fflush(stdout); + + test_instances_t group_elements = + generate_group_elements(10, 1 << expn); + test_instances_t scalars = + generate_scalars(10, 1 << expn); + + run_result_t result_bos_coster = + profile_multiexp( + group_elements, scalars); + printf("\t%lld", result_bos_coster.first); fflush(stdout); + + run_result_t result_djb = + profile_multiexp( + group_elements, scalars); + printf("\t%lld", result_djb.first); fflush(stdout); + + if (compare_answers && (result_bos_coster.second != result_djb.second)) { + fprintf(stderr, "Answers NOT MATCHING (bos coster != djb)\n"); + } + + if (expn <= expn_end_naive) { + run_result_t result_naive = + profile_multiexp( + group_elements, scalars); + printf("\t%lld", result_naive.first); fflush(stdout); + + if (compare_answers && (result_bos_coster.second != result_naive.second)) { + fprintf(stderr, "Answers NOT MATCHING (bos coster != naive)\n"); + } + } + + printf("\n"); + } +} + +int main(void) +{ + print_compilation_info(); + + printf("Profiling BN128_G1\n"); + bn128_pp::init_public_params(); + print_performance_csv, Fr >(2, 20, 14, true); + + printf("Profiling BN128_G2\n"); + print_performance_csv, Fr >(2, 20, 14, true); + + return 0; +}