Clearmatics Libff  0.1
C++ library for Finite Fields and Elliptic Curves
multiexp_stream.tcc
Go to the documentation of this file.
1 /** @file
2  *****************************************************************************
3  * @author This file is part of libff, developed by Clearmatics Ltd
4  * (originally developed by SCIPR Lab) and contributors
5  * (see AUTHORS).
6  * @copyright MIT license (see LICENSE file)
7  *****************************************************************************/
8 
9 #ifndef MULTIEXP_STREAM_TCC_
10 #define MULTIEXP_STREAM_TCC_
11 
12 #include "libff/algebra/scalar_multiplication/multiexp.hpp"
13 
14 namespace libff
15 {
16 
17 template<form_t Form, compression_t Comp, typename GroupT>
18 void elements_from_stream_producer(
19  std::istream &in_s,
20  concurrent_fifo_spsc<GroupT> &fifo,
21  const size_t num_entries)
22 {
23  // Read and enqueue all entries from the stream
24  for (size_t element_idx = 0; element_idx < num_entries; ++element_idx) {
25  GroupT *dest = fifo.enqueue_begin_wait();
26  group_read<encoding_binary, Form, Comp>(*dest, in_s);
27  fifo.enqueue_end();
28  }
29 }
30 
31 /// Read blocks of elements and enqueue them as buffers into a
32 /// concurrent_buffer_fifo_spsc.
33 template<form_t Form, compression_t Comp, typename GroupT>
34 void element_buffers_from_stream_producer(
35  std::istream &in_s,
36  concurrent_buffer_fifo_spsc<GroupT> &fifo,
37  const size_t num_buffers,
38  const size_t num_elements_per_buffer)
39 {
40  // Read and enqueue each buffer
41  for (size_t element_idx = 0; element_idx < num_buffers; ++element_idx) {
42  GroupT *dest = fifo.enqueue_begin_wait();
43  GroupT *const dest_end = dest + num_elements_per_buffer;
44  for (; dest < dest_end; ++dest) {
45  group_read<encoding_binary, Form, Comp>(*dest, in_s);
46  }
47  fifo.enqueue_end();
48  }
49 }
50 
51 template<typename GroupT, typename FieldT>
52 GroupT multi_exp_base_elements_from_fifo_all_rounds(
53  concurrent_fifo_spsc<GroupT> &fifo,
54  const std::vector<FieldT> &exponents,
55  const size_t c)
56 {
57  const size_t num_entries = exponents.size();
58  // Allow sufficient rounds for num_bits + 2, to accomodate overflow +
59  // negative final digit.
60  const size_t num_digits = (FieldT::num_bits + 2 + c - 1) / c;
61  const size_t num_buckets = 1 << (c - 1);
62 
63  // Allocate state for all rounds.
64  std::vector<std::vector<GroupT>> round_buckets(num_digits);
65  std::vector<std::vector<bool>> round_bucket_hit(num_digits);
66  for (std::vector<GroupT> &buckets : round_buckets) {
67  buckets.resize(num_buckets);
68  }
69  for (std::vector<bool> &bucket_hit : round_bucket_hit) {
70  bucket_hit.resize(num_buckets);
71  }
72  std::vector<ssize_t> digits(num_digits);
73 
74  // Process each element
75  for (size_t el_idx = 0; el_idx < num_entries; ++el_idx) {
76  // Decompose the scalar, and wait for an element from the fifo
77  field_get_signed_digits(digits, exponents[el_idx], c, num_digits);
78  const GroupT &group_element = *(fifo.dequeue_begin_wait());
79 
80  // Process all digits
81  for (size_t digit_idx = 0; digit_idx < num_digits; ++digit_idx) {
82  std::vector<GroupT> &buckets = round_buckets[digit_idx];
83  std::vector<bool> &bucket_hit = round_bucket_hit[digit_idx];
84  const ssize_t digit = digits[digit_idx];
85 
86  internal::multi_exp_add_element_to_bucket_with_signed_digit<
87  GroupT,
88  multi_exp_base_form_special>(
89  buckets, bucket_hit, group_element, digit);
90  }
91 
92  fifo.dequeue_end();
93  }
94 
95  // For each digit, sum the buckets and accumulate the total
96  GroupT result = internal::
97  multiexp_accumulate_buckets<GroupT, multi_exp_base_form_normal>(
98  round_buckets[num_digits - 1],
99  round_bucket_hit[num_digits - 1],
100  num_buckets);
101  round_buckets[num_digits - 1].clear();
102  round_bucket_hit[num_digits - 1].clear();
103 
104  for (size_t digit_idx = num_digits - 2; digit_idx < num_digits;
105  --digit_idx) {
106  for (size_t i = 0; i < c; ++i) {
107  result = result.dbl();
108  }
109 
110  const GroupT digit_sum = internal::
111  multiexp_accumulate_buckets<GroupT, multi_exp_base_form_normal>(
112  round_buckets[digit_idx],
113  round_bucket_hit[digit_idx],
114  num_buckets);
115  round_buckets[digit_idx].clear();
116  round_bucket_hit[digit_idx].clear();
117 
118  result = result + digit_sum;
119  }
120 
121  return result;
122 }
123 
124 template<typename GroupT, typename FieldT>
125 GroupT multi_exp_precompute_from_fifo(
126  concurrent_buffer_fifo_spsc<GroupT> &fifo,
127  const std::vector<FieldT> &exponents,
128  const size_t c,
129  const size_t num_digits)
130 {
131  const size_t num_entries = exponents.size();
132  const size_t num_buckets = 1 << (c - 1);
133 
134  // Allocate state (single collection of buckets).
135  std::vector<GroupT> buckets(num_buckets);
136  std::vector<bool> bucket_hit(num_buckets, false);
137  std::vector<ssize_t> digits(num_digits);
138 
139  // Process each element
140  for (size_t el_idx = 0; el_idx < num_entries; ++el_idx) {
141  // Decompose the scalar into the signed digits
142  field_get_signed_digits(digits, exponents[el_idx], c, num_digits);
143 
144  // Wait for the precomputed data
145  const GroupT *const precomputed = fifo.dequeue_begin_wait();
146 
147  // Process all digits, using the preceomputed data
148  for (size_t digit_idx = 0; digit_idx < num_digits; ++digit_idx) {
149  internal::multi_exp_add_element_to_bucket_with_signed_digit<
150  GroupT,
151  multi_exp_base_form_special>(
152  buckets, bucket_hit, precomputed[digit_idx], digits[digit_idx]);
153  }
154 
155  fifo.dequeue_end();
156  }
157 
158  // Sum the buckets
159  return internal::
160  multiexp_accumulate_buckets<GroupT, multi_exp_base_form_normal>(
161  buckets, bucket_hit, num_buckets);
162 }
163 
164 template<form_t Form, compression_t Comp, typename GroupT, typename FieldT>
165 GroupT multi_exp_stream(
166  std::istream &base_elements_in, const std::vector<FieldT> &exponents)
167 {
168  static const size_t FIFO_SIZE = 1024;
169  const size_t num_entries = exponents.size();
170  const size_t c = bdlo12_signed_optimal_c(num_entries);
171  assert(c > 0);
172 
173  // Fifo for streaming
174  concurrent_fifo_spsc<GroupT> fifo(FIFO_SIZE);
175 
176  // Launch the reading thread
177  std::thread producer([&base_elements_in, &fifo, num_entries]() {
178  elements_from_stream_producer<Form, Comp, GroupT>(
179  base_elements_in, fifo, num_entries);
180  });
181 
182  // Consume all elements from the fifo.
183  const GroupT result =
184  multi_exp_base_elements_from_fifo_all_rounds<GroupT, FieldT>(
185  fifo, exponents, c);
186 
187  // Wait for reading thread
188  producer.join();
189 
190  return result;
191 }
192 
193 template<form_t Form, compression_t Comp, typename GroupT, typename FieldT>
194 GroupT multi_exp_stream_with_precompute(
195  std::istream &base_elements_in,
196  const std::vector<FieldT> &exponents,
197  const size_t c)
198 {
199  // Each entry is a buffer. We may want to tweak this (possibly depending on
200  // exponents.size() and precompute_c), but for now we expect 8 to be a
201  // reasonable number to avoid too much blocking.
202  static const size_t FIFO_SIZE = 8;
203  const size_t num_entries = exponents.size();
204  const size_t num_digits = (FieldT::num_bits + c - 1) / c;
205 
206  // Construct the fifo and begin reading buffers. The fifo has size
207  // FIFO_SIZE where each entry is all the precomputed values for a single
208  // base element (i.e. the multiple for each digit).
209  concurrent_buffer_fifo_spsc<GroupT> fifo(FIFO_SIZE, num_digits);
210 
211  std::thread producer([&base_elements_in, &fifo, num_entries, num_digits]() {
212  element_buffers_from_stream_producer<Form, Comp, GroupT>(
213  base_elements_in, fifo, num_entries, num_digits);
214  });
215 
216  // Consume all elements from the fifo to compute the final result.
217  const GroupT result = multi_exp_precompute_from_fifo<GroupT, FieldT>(
218  fifo, exponents, c, num_digits);
219 
220  producer.join();
221 
222  return result;
223 }
224 
225 } // namespace libff
226 
227 #endif // MULTIEXP_STREAM_TCC_