Skip to content

Commit 0f4b380

Browse files
authored
Merge pull request #81 from popoffka/multiexp-perf
Adapt to new libff multiexp interface & use faster methods
2 parents fbe774e + 6b9fd10 commit 0f4b380

File tree

11 files changed

+343
-189
lines changed

11 files changed

+343
-189
lines changed

libsnark/common/data_structures/sparse_vector.tcc

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616

1717
#include <numeric>
1818

19+
#ifdef MULTICORE
20+
#include <omp.h>
21+
#endif
22+
1923
#include <libff/algebra/scalar_multiplication/multiexp.hpp>
2024

2125
namespace libsnark {
@@ -180,9 +184,11 @@ std::pair<T, sparse_vector<T> > sparse_vector<T>::accumulate(const typename std:
180184
const typename std::vector<FieldT>::const_iterator &it_end,
181185
const size_t offset) const
182186
{
183-
// TODO: does not really belong here.
187+
#ifdef MULTICORE
188+
const size_t chunks = omp_get_max_threads(); // to override, set OMP_NUM_THREADS env var or call omp_set_num_threads()
189+
#else
184190
const size_t chunks = 1;
185-
const bool use_multiexp = true;
191+
#endif
186192

187193
T accumulated_value = T::zero();
188194
sparse_vector<T> resulting_vector;
@@ -215,11 +221,12 @@ std::pair<T, sparse_vector<T> > sparse_vector<T>::accumulate(const typename std:
215221
#ifdef DEBUG
216222
libff::print_indent(); printf("doing multiexp for w_%zu ... w_%zu\n", indices[first_pos], indices[last_pos]);
217223
#endif
218-
accumulated_value = accumulated_value + libff::multi_exp<T, FieldT>(values.begin() + first_pos,
219-
values.begin() + last_pos + 1,
220-
it_begin + (indices[first_pos] - offset),
221-
it_begin + (indices[last_pos] - offset) + 1,
222-
chunks, use_multiexp);
224+
accumulated_value = accumulated_value + libff::multi_exp<T, FieldT, libff::multi_exp_method_bos_coster>(
225+
values.begin() + first_pos,
226+
values.begin() + last_pos + 1,
227+
it_begin + (indices[first_pos] - offset),
228+
it_begin + (indices[last_pos] - offset) + 1,
229+
chunks);
223230
}
224231
}
225232
else
@@ -250,11 +257,12 @@ std::pair<T, sparse_vector<T> > sparse_vector<T>::accumulate(const typename std:
250257
#ifdef DEBUG
251258
libff::print_indent(); printf("doing multiexp for w_%zu ... w_%zu\n", indices[first_pos], indices[last_pos]);
252259
#endif
253-
accumulated_value = accumulated_value + libff::multi_exp<T, FieldT>(values.begin() + first_pos,
254-
values.begin() + last_pos + 1,
255-
it_begin + (indices[first_pos] - offset),
256-
it_begin + (indices[last_pos] - offset) + 1,
257-
chunks, use_multiexp);
260+
accumulated_value = accumulated_value + libff::multi_exp<T, FieldT, libff::multi_exp_method_bos_coster>(
261+
values.begin() + first_pos,
262+
values.begin() + last_pos + 1,
263+
it_begin + (indices[first_pos] - offset),
264+
it_begin + (indices[last_pos] - offset) + 1,
265+
chunks);
258266
}
259267

260268
return std::make_pair(accumulated_value, resulting_vector);

libsnark/knowledge_commitment/kc_multiexp.hpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
Will probably go away in more general exp refactoring.
1818
*/
1919

20+
#include <libff/algebra/scalar_multiplication/multiexp.hpp>
21+
2022
#include <libsnark/knowledge_commitment/knowledge_commitment.hpp>
2123

2224
namespace libsnark {
@@ -25,17 +27,13 @@ template<typename T1, typename T2, mp_size_t n>
2527
knowledge_commitment<T1,T2> opt_window_wnaf_exp(const knowledge_commitment<T1,T2> &base,
2628
const libff::bigint<n> &scalar, const size_t scalar_bits);
2729

28-
template<typename T1, typename T2, typename FieldT>
30+
template<typename T1, typename T2, typename FieldT, libff::multi_exp_method Method>
2931
knowledge_commitment<T1, T2> kc_multi_exp_with_mixed_addition(const knowledge_commitment_vector<T1, T2> &vec,
3032
const size_t min_idx,
3133
const size_t max_idx,
3234
typename std::vector<FieldT>::const_iterator scalar_start,
3335
typename std::vector<FieldT>::const_iterator scalar_end,
34-
const size_t chunks,
35-
const bool use_multiexp=false);
36-
37-
template<typename T1, typename T2>
38-
void kc_batch_to_special(std::vector<knowledge_commitment<T1, T2> > &vec);
36+
const size_t chunks);
3937

4038
template<typename T1, typename T2, typename FieldT>
4139
knowledge_commitment_vector<T1, T2> kc_batch_exp(const size_t scalar_size,

libsnark/knowledge_commitment/kc_multiexp.tcc

Lines changed: 4 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ knowledge_commitment<T1,T2> opt_window_wnaf_exp(const knowledge_commitment<T1,T2
1818
opt_window_wnaf_exp(base.h, scalar, scalar_bits));
1919
}
2020

21-
template<typename T1, typename T2, typename FieldT>
21+
template<typename T1, typename T2, typename FieldT, libff::multi_exp_method Method>
2222
knowledge_commitment<T1, T2> kc_multi_exp_with_mixed_addition(const knowledge_commitment_vector<T1, T2> &vec,
2323
const size_t min_idx,
2424
const size_t max_idx,
2525
typename std::vector<FieldT>::const_iterator scalar_start,
2626
typename std::vector<FieldT>::const_iterator scalar_end,
27-
const size_t chunks,
28-
const bool use_multiexp)
27+
const size_t chunks)
2928
{
3029
libff::enter_block("Process scalar vector");
3130
auto index_it = std::lower_bound(vec.indices.begin(), vec.indices.end(), min_idx);
@@ -86,77 +85,7 @@ knowledge_commitment<T1, T2> kc_multi_exp_with_mixed_addition(const knowledge_co
8685
libff::print_indent(); printf("* Elements of w remaining: %zu (%0.2f%%)\n", num_other, 100.*num_other/(num_skip+num_add+num_other));
8786
libff::leave_block("Process scalar vector");
8887

89-
return acc + libff::multi_exp<knowledge_commitment<T1, T2>, FieldT>(g.begin(), g.end(), p.begin(), p.end(), chunks, use_multiexp);
90-
}
91-
92-
template<typename T1, typename T2>
93-
void kc_batch_to_special(std::vector<knowledge_commitment<T1, T2> > &vec)
94-
{
95-
libff::enter_block("Batch-convert knowledge-commitments to special form");
96-
97-
std::vector<T1> g_vec;
98-
g_vec.reserve(vec.size());
99-
100-
for (size_t i = 0; i < vec.size(); ++i)
101-
{
102-
if (!vec[i].g.is_zero())
103-
{
104-
g_vec.emplace_back(vec[i].g);
105-
}
106-
}
107-
108-
libff::batch_to_special_all_non_zeros<T1>(g_vec);
109-
auto g_it = g_vec.begin();
110-
T1 T1_zero_special = T1::zero();
111-
T1_zero_special.to_special();
112-
113-
for (size_t i = 0; i < vec.size(); ++i)
114-
{
115-
if (!vec[i].g.is_zero())
116-
{
117-
vec[i].g = *g_it;
118-
++g_it;
119-
}
120-
else
121-
{
122-
vec[i].g = T1_zero_special;
123-
}
124-
}
125-
126-
g_vec.clear();
127-
128-
std::vector<T2> h_vec;
129-
h_vec.reserve(vec.size());
130-
131-
for (size_t i = 0; i < vec.size(); ++i)
132-
{
133-
if (!vec[i].h.is_zero())
134-
{
135-
h_vec.emplace_back(vec[i].h);
136-
}
137-
}
138-
139-
libff::batch_to_special_all_non_zeros<T2>(h_vec);
140-
auto h_it = h_vec.begin();
141-
T2 T2_zero_special = T2::zero();
142-
T2_zero_special.to_special();
143-
144-
for (size_t i = 0; i < vec.size(); ++i)
145-
{
146-
if (!vec[i].h.is_zero())
147-
{
148-
vec[i].h = *h_it;
149-
++h_it;
150-
}
151-
else
152-
{
153-
vec[i].h = T2_zero_special;
154-
}
155-
}
156-
157-
g_vec.clear();
158-
159-
libff::leave_block("Batch-convert knowledge-commitments to special form");
88+
return acc + libff::multi_exp<knowledge_commitment<T1, T2>, FieldT, Method>(g.begin(), g.end(), p.begin(), p.end(), chunks);
16089
}
16190

16291
template<typename T1, typename T2, typename FieldT>
@@ -249,7 +178,7 @@ knowledge_commitment_vector<T1, T2> kc_batch_exp(const size_t scalar_size,
249178
tmp[i] = kc_batch_exp_internal<T1, T2, FieldT>(scalar_size, T1_window, T2_window, T1_table, T2_table, T1_coeff, T2_coeff, v,
250179
chunk_pos[i], chunk_pos[i+1], i == num_chunks - 1 ? last_chunk : chunk_size);
251180
#ifdef USE_MIXED_ADDITION
252-
kc_batch_to_special<T1, T2>(tmp[i].values);
181+
libff::batch_to_special<knowledge_commitment<T1, T2>>(tmp[i].values);
253182
#endif
254183
}
255184

libsnark/knowledge_commitment/knowledge_commitment.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ struct knowledge_commitment {
4545
knowledge_commitment<T1,T2>& operator=(const knowledge_commitment<T1,T2> &other) = default;
4646
knowledge_commitment<T1,T2>& operator=(knowledge_commitment<T1,T2> &&other) = default;
4747
knowledge_commitment<T1,T2> operator+(const knowledge_commitment<T1, T2> &other) const;
48+
knowledge_commitment<T1,T2> mixed_add(const knowledge_commitment<T1, T2> &other) const;
49+
knowledge_commitment<T1,T2> dbl() const;
50+
51+
void to_special();
52+
bool is_special() const;
4853

4954
bool is_zero() const;
5055
bool operator==(const knowledge_commitment<T1,T2> &other) const;
@@ -56,6 +61,9 @@ struct knowledge_commitment {
5661
void print() const;
5762

5863
static size_t size_in_bits();
64+
65+
static void batch_to_special_all_non_zeros(
66+
std::vector<knowledge_commitment<T1,T2> > &vec);
5967
};
6068

6169
template<typename T1, typename T2, mp_size_t m>

libsnark/knowledge_commitment/knowledge_commitment.tcc

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,33 @@ knowledge_commitment<T1,T2> knowledge_commitment<T1,T2>::operator+(const knowled
4343
this->h + other.h);
4444
}
4545

46+
template<typename T1, typename T2>
47+
knowledge_commitment<T1,T2> knowledge_commitment<T1,T2>::mixed_add(const knowledge_commitment<T1,T2> &other) const
48+
{
49+
return knowledge_commitment<T1,T2>(this->g.mixed_add(other.g),
50+
this->h.mixed_add(other.h));
51+
}
52+
53+
template<typename T1, typename T2>
54+
knowledge_commitment<T1,T2> knowledge_commitment<T1,T2>::dbl() const
55+
{
56+
return knowledge_commitment<T1,T2>(this->g.dbl(),
57+
this->h.dbl());
58+
}
59+
60+
template<typename T1, typename T2>
61+
void knowledge_commitment<T1,T2>::to_special()
62+
{
63+
this->g.to_special();
64+
this->h.to_special();
65+
}
66+
67+
template<typename T1, typename T2>
68+
bool knowledge_commitment<T1,T2>::is_special() const
69+
{
70+
return this->g->is_special() && this->h->is_special();
71+
}
72+
4673
template<typename T1, typename T2>
4774
bool knowledge_commitment<T1,T2>::is_zero() const
4875
{
@@ -106,6 +133,80 @@ std::istream& operator>>(std::istream& in, knowledge_commitment<T1,T2> &kc)
106133
return in;
107134
}
108135

136+
template<typename T1, typename T2>
137+
void knowledge_commitment<T1,T2>::batch_to_special_all_non_zeros(
138+
std::vector<knowledge_commitment<T1,T2> > &vec)
139+
{
140+
// it is guaranteed that every vec[i] is non-zero,
141+
// but, for any i, *one* of vec[i].g and vec[i].h might still be zero,
142+
// so we still have to handle zeros separately
143+
144+
// we separately process g's first, then h's
145+
// to lower memory consumption
146+
std::vector<T1> g_vec;
147+
g_vec.reserve(vec.size());
148+
149+
for (size_t i = 0; i < vec.size(); ++i)
150+
{
151+
if (!vec[i].g.is_zero())
152+
{
153+
g_vec.emplace_back(vec[i].g);
154+
}
155+
}
156+
157+
T1::batch_to_special_all_non_zeros(g_vec);
158+
auto g_it = g_vec.begin();
159+
T1 T1_zero_special = T1::zero();
160+
T1_zero_special.to_special();
161+
162+
for (size_t i = 0; i < vec.size(); ++i)
163+
{
164+
if (!vec[i].g.is_zero())
165+
{
166+
vec[i].g = *g_it;
167+
++g_it;
168+
}
169+
else
170+
{
171+
vec[i].g = T1_zero_special;
172+
}
173+
}
174+
175+
g_vec.clear();
176+
177+
// exactly the same thing, but for h:
178+
std::vector<T2> h_vec;
179+
h_vec.reserve(vec.size());
180+
181+
for (size_t i = 0; i < vec.size(); ++i)
182+
{
183+
if (!vec[i].h.is_zero())
184+
{
185+
h_vec.emplace_back(vec[i].h);
186+
}
187+
}
188+
189+
T2::batch_to_special_all_non_zeros(h_vec);
190+
auto h_it = h_vec.begin();
191+
T2 T2_zero_special = T2::zero();
192+
T2_zero_special.to_special();
193+
194+
for (size_t i = 0; i < vec.size(); ++i)
195+
{
196+
if (!vec[i].h.is_zero())
197+
{
198+
vec[i].h = *h_it;
199+
++h_it;
200+
}
201+
else
202+
{
203+
vec[i].h = T2_zero_special;
204+
}
205+
}
206+
207+
h_vec.clear();
208+
}
209+
109210
} // libsnark
110211

111212
#endif // KNOWLEDGE_COMMITMENT_TCC_

libsnark/relations/arithmetic_programs/qap/qap.tcc

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -242,14 +242,22 @@ bool qap_instance_evaluation<FieldT>::is_satisfied(const qap_witness<FieldT> &wi
242242
FieldT ans_C = this->Ct[0] + witness.d3*this->Zt;
243243
FieldT ans_H = FieldT::zero();
244244

245-
ans_A = ans_A + libff::naive_plain_exp<FieldT, FieldT>(this->At.begin()+1, this->At.begin()+1+this->num_variables(),
246-
witness.coefficients_for_ABCs.begin(), witness.coefficients_for_ABCs.begin()+this->num_variables());
247-
ans_B = ans_B + libff::naive_plain_exp<FieldT, FieldT>(this->Bt.begin()+1, this->Bt.begin()+1+this->num_variables(),
248-
witness.coefficients_for_ABCs.begin(), witness.coefficients_for_ABCs.begin()+this->num_variables());
249-
ans_C = ans_C + libff::naive_plain_exp<FieldT, FieldT>(this->Ct.begin()+1, this->Ct.begin()+1+this->num_variables(),
250-
witness.coefficients_for_ABCs.begin(), witness.coefficients_for_ABCs.begin()+this->num_variables());
251-
ans_H = ans_H + libff::naive_plain_exp<FieldT, FieldT>(this->Ht.begin(), this->Ht.begin()+this->degree()+1,
252-
witness.coefficients_for_H.begin(), witness.coefficients_for_H.begin()+this->degree()+1);
245+
ans_A = ans_A + libff::inner_product<FieldT>(this->At.begin()+1,
246+
this->At.begin()+1+this->num_variables(),
247+
witness.coefficients_for_ABCs.begin(),
248+
witness.coefficients_for_ABCs.begin()+this->num_variables());
249+
ans_B = ans_B + libff::inner_product<FieldT>(this->Bt.begin()+1,
250+
this->Bt.begin()+1+this->num_variables(),
251+
witness.coefficients_for_ABCs.begin(),
252+
witness.coefficients_for_ABCs.begin()+this->num_variables());
253+
ans_C = ans_C + libff::inner_product<FieldT>(this->Ct.begin()+1,
254+
this->Ct.begin()+1+this->num_variables(),
255+
witness.coefficients_for_ABCs.begin(),
256+
witness.coefficients_for_ABCs.begin()+this->num_variables());
257+
ans_H = ans_H + libff::inner_product<FieldT>(this->Ht.begin(),
258+
this->Ht.begin()+this->degree()+1,
259+
witness.coefficients_for_H.begin(),
260+
witness.coefficients_for_H.begin()+this->degree()+1);
253261

254262
if (ans_A * ans_B - ans_C != ans_H * this->Zt)
255263
{

libsnark/relations/arithmetic_programs/ssp/ssp.tcc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,14 @@ bool ssp_instance_evaluation<FieldT>::is_satisfied(const ssp_witness<FieldT> &wi
209209
FieldT ans_V = this->Vt[0] + witness.d*this->Zt;
210210
FieldT ans_H = FieldT::zero();
211211

212-
ans_V = ans_V + libff::naive_plain_exp<FieldT, FieldT>(this->Vt.begin()+1, this->Vt.begin()+1+this->num_variables(),
213-
witness.coefficients_for_Vs.begin(), witness.coefficients_for_Vs.begin()+this->num_variables());
214-
ans_H = ans_H + libff::naive_plain_exp<FieldT, FieldT>(this->Ht.begin(), this->Ht.begin()+this->degree()+1,
215-
witness.coefficients_for_H.begin(), witness.coefficients_for_H.begin()+this->degree()+1);
212+
ans_V = ans_V + libff::inner_product<FieldT>(this->Vt.begin()+1,
213+
this->Vt.begin()+1+this->num_variables(),
214+
witness.coefficients_for_Vs.begin(),
215+
witness.coefficients_for_Vs.begin()+this->num_variables());
216+
ans_H = ans_H + libff::inner_product<FieldT>(this->Ht.begin(),
217+
this->Ht.begin()+this->degree()+1,
218+
witness.coefficients_for_H.begin(),
219+
witness.coefficients_for_H.begin()+this->degree()+1);
216220

217221
if (ans_V.squared() - FieldT::one() != ans_H * this->Zt)
218222
{

0 commit comments

Comments
 (0)