/*
* Same multiexp algorithm used in Bellman, with some modifications.
* https://github.com/zkcrypto/bellman/blob/10c5010fd9c2ca69442dc9775ea271e286e776d8/src/multiexp.rs#L174
* The CPU version of multiexp parallelism is done by dividing the exponent
* values into smaller windows, and then applying a sequence of rounds to each
* window. The GPU kernel not only assigns a thread to each window but also
* divides the bases into several groups which highly increases the number of
* threads running in parallel for calculating a multiexp instance.
*/
KERNEL void POINT_multiexp(
GLOBAL POINT_affine *bases,
GLOBAL POINT_jacobian *buckets,
GLOBAL POINT_jacobian *results,
GLOBAL EXPONENT *exps,
uint n,
uint num_groups,
uint num_windows,
uint window_size) {
// We have `num_windows` * `num_groups` threads per multiexp.
const uint gid = GET_GLOBAL_ID() if(gid >= num_windows * num_groups) return
// We have (2^window_size - 1) buckets.
const uint bucket_len = ((1 << window_size) - 1)
// Each thread has its own set of buckets in global memory.
buckets += bucket_len * gid
const POINT_jacobian local_zero = POINT_ZERO for(uint i = 0
// Num of elements in each group. Round the number up (ceil).
const uint len = (n + num_groups - 1) / num_groups
// This thread runs the multiexp algorithm on elements from `nstart` to `nened`
// on the window [`bits`, `bits` + `w`)
const uint nstart = len * (gid / num_windows) const uint nend = min(nstart + len, n) const uint bits = (gid % num_windows) * window_size const ushort w = min((ushort)window_size, (ushort)(EXPONENT_BITS - bits))
POINT_jacobian res = POINT_ZERO for(uint i = nstart uint ind = EXPONENT_get_bits(exps[i], bits, w)
#if defined(OPENCL_NVIDIA) || defined(CUDA)
// O_o, weird optimization, having a single special case makes it
// tremendously faster!
// 511 is chosen because it's half of the maximum bucket len, but
// any other number works... Bigger indices seems to be better...
if(ind == 511) buckets[510] = POINT_add_mixed(buckets[510], bases[i]) else if(ind--) buckets[ind] = POINT_add_mixed(buckets[ind], bases[i]) #else
if(ind--) buckets[ind] = POINT_add_mixed(buckets[ind], bases[i]) #endif
}
// Summation by parts
// e.g. 3a + 2b + 1c = a +
// (a) + b +
// ((a) + b) + c
POINT_jacobian acc = POINT_ZERO for(int j = bucket_len - 1 acc = POINT_add(acc, buckets[j]) res = POINT_add(res, acc) }
results[gid] = res}