2 *****************************************************************************
3 * @author This file is part of libff, developed by Clearmatics Ltd
4 * (originally developed by SCIPR Lab) and contributors
6 * @copyright MIT license (see LICENSE file)
7 *****************************************************************************/
9 #ifndef MULTIEXP_STREAM_TCC_
10 #define MULTIEXP_STREAM_TCC_
12 #include "libff/algebra/scalar_multiplication/multiexp.hpp"
17 template<form_t Form, compression_t Comp, typename GroupT>
18 void elements_from_stream_producer(
20 concurrent_fifo_spsc<GroupT> &fifo,
21 const size_t num_entries)
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);
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(
36 concurrent_buffer_fifo_spsc<GroupT> &fifo,
37 const size_t num_buffers,
38 const size_t num_elements_per_buffer)
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);
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,
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);
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);
69 for (std::vector<bool> &bucket_hit : round_bucket_hit) {
70 bucket_hit.resize(num_buckets);
72 std::vector<ssize_t> digits(num_digits);
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());
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];
86 internal::multi_exp_add_element_to_bucket_with_signed_digit<
88 multi_exp_base_form_special>(
89 buckets, bucket_hit, group_element, digit);
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],
101 round_buckets[num_digits - 1].clear();
102 round_bucket_hit[num_digits - 1].clear();
104 for (size_t digit_idx = num_digits - 2; digit_idx < num_digits;
106 for (size_t i = 0; i < c; ++i) {
107 result = result.dbl();
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],
115 round_buckets[digit_idx].clear();
116 round_bucket_hit[digit_idx].clear();
118 result = result + digit_sum;
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,
129 const size_t num_digits)
131 const size_t num_entries = exponents.size();
132 const size_t num_buckets = 1 << (c - 1);
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);
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);
144 // Wait for the precomputed data
145 const GroupT *const precomputed = fifo.dequeue_begin_wait();
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<
151 multi_exp_base_form_special>(
152 buckets, bucket_hit, precomputed[digit_idx], digits[digit_idx]);
160 multiexp_accumulate_buckets<GroupT, multi_exp_base_form_normal>(
161 buckets, bucket_hit, num_buckets);
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)
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);
173 // Fifo for streaming
174 concurrent_fifo_spsc<GroupT> fifo(FIFO_SIZE);
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);
182 // Consume all elements from the fifo.
183 const GroupT result =
184 multi_exp_base_elements_from_fifo_all_rounds<GroupT, FieldT>(
187 // Wait for reading thread
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,
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;
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);
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);
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);
227 #endif // MULTIEXP_STREAM_TCC_