aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ringct/bulletproofs.cc11
-rw-r--r--src/ringct/multiexp.cc80
-rw-r--r--src/ringct/multiexp.h7
-rw-r--r--tests/performance_tests/main.cpp7
-rw-r--r--tests/performance_tests/multiexp.h7
-rw-r--r--tests/unit_tests/CMakeLists.txt1
-rw-r--r--tests/unit_tests/multiexp.cpp149
7 files changed, 230 insertions, 32 deletions
diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc
index 1c29b1b99..6ba984b03 100644
--- a/src/ringct/bulletproofs.cc
+++ b/src/ringct/bulletproofs.cc
@@ -61,6 +61,7 @@ static constexpr size_t maxM = 16;
static rct::key Hi[maxN*maxM], Gi[maxN*maxM];
static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM];
static ge_dsmp Gprecomp[maxN*maxM], Hprecomp[maxN*maxM];
+static std::shared_ptr<straus_cached_data> HiGi_cache;
static const rct::key TWO = { {0x02, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
static const rct::keyV oneN = vector_dup(rct::identity(), maxN);
static const rct::keyV twoN = vector_powers(TWO, maxN);
@@ -70,7 +71,7 @@ static boost::mutex init_mutex;
static inline rct::key multiexp(const std::vector<MultiexpData> &data, bool HiGi)
{
if (HiGi || data.size() < 1000)
- return straus(data, HiGi);
+ return straus(data, HiGi ? HiGi_cache: NULL);
else
return bos_coster_heap_conv_robust(data);
}
@@ -116,6 +117,7 @@ static void init_exponents()
static bool init_done = false;
if (init_done)
return;
+ std::vector<MultiexpData> data;
for (size_t i = 0; i < maxN*maxM; ++i)
{
Hi[i] = get_exponent(rct::H, i * 2);
@@ -124,8 +126,13 @@ static void init_exponents()
Gi[i] = get_exponent(rct::H, i * 2 + 1);
rct::precomp(Gprecomp[i], Gi[i]);
CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Gi_p3[i], Gi[i].bytes) == 0, "ge_frombytes_vartime failed");
+
+ data.push_back({rct::zero(), Gi[i]});
+ data.push_back({rct::zero(), Hi[i]});
}
- MINFO("cache size: " << (sizeof(Hi)+sizeof(Hprecomp)+sizeof(Hi_p3))*2/1024 << " kB");
+ HiGi_cache = straus_init_cache(data);
+ size_t cache_size = (sizeof(Hi)+sizeof(Hprecomp)+sizeof(Hi_p3))*2 + straus_get_cache_size(HiGi_cache);
+ MINFO("cache size: " << cache_size/1024 << " kB");
init_done = true;
}
diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc
index 7ed9672f2..4f16bd588 100644
--- a/src/ringct/multiexp.cc
+++ b/src/ringct/multiexp.cc
@@ -259,42 +259,66 @@ rct::key bos_coster_heap_conv_robust(std::vector<MultiexpData> data)
return res;
}
-rct::key straus(const std::vector<MultiexpData> &data, bool HiGi)
+struct straus_cached_data
{
- MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000));
+ std::vector<std::vector<ge_cached>> multiples;
+};
- MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000));
- static constexpr unsigned int c = 4;
- static constexpr unsigned int mask = (1<<c)-1;
- static std::vector<std::vector<ge_cached>> HiGi_multiples;
- std::vector<std::vector<ge_cached>> local_multiples, &multiples = HiGi ? HiGi_multiples : local_multiples;
+static constexpr unsigned int STRAUS_C = 4;
+
+std::shared_ptr<straus_cached_data> straus_init_cache(const std::vector<MultiexpData> &data)
+{
+ MULTIEXP_PERF(PERF_TIMER_START_UNIT(multiples, 1000000));
ge_cached cached;
ge_p1p1 p1;
ge_p3 p3;
+ std::shared_ptr<straus_cached_data> cache(new straus_cached_data());
- std::vector<uint8_t> skip(data.size());
- for (size_t i = 0; i < data.size(); ++i)
- skip[i] = data[i].scalar == rct::zero() || !memcmp(&data[i].point, &ge_p3_identity, sizeof(ge_p3));
-
- MULTIEXP_PERF(PERF_TIMER_START_UNIT(multiples, 1000000));
- multiples.resize(1<<c);
- size_t offset = multiples[1].size();
- multiples[1].resize(std::max(offset, data.size()));
+ cache->multiples.resize(1<<STRAUS_C);
+ size_t offset = cache->multiples[1].size();
+ cache->multiples[1].resize(std::max(offset, data.size()));
for (size_t i = offset; i < data.size(); ++i)
- ge_p3_to_cached(&multiples[1][i], &data[i].point);
- for (size_t i=2;i<1<<c;++i)
- multiples[i].resize(std::max(offset, data.size()));
+ ge_p3_to_cached(&cache->multiples[1][i], &data[i].point);
+ for (size_t i=2;i<1<<STRAUS_C;++i)
+ cache->multiples[i].resize(std::max(offset, data.size()));
for (size_t j=offset;j<data.size();++j)
{
- for (size_t i=2;i<1<<c;++i)
+ for (size_t i=2;i<1<<STRAUS_C;++i)
{
- ge_add(&p1, &data[j].point, &multiples[i-1][j]);
+ ge_add(&p1, &data[j].point, &cache->multiples[i-1][j]);
ge_p1p1_to_p3(&p3, &p1);
- ge_p3_to_cached(&multiples[i][j], &p3);
+ ge_p3_to_cached(&cache->multiples[i][j], &p3);
}
}
MULTIEXP_PERF(PERF_TIMER_STOP(multiples));
+ return cache;
+}
+
+size_t straus_get_cache_size(const std::shared_ptr<straus_cached_data> &cache)
+{
+ size_t sz = 0;
+ for (const auto &e0: cache->multiples)
+ sz += e0.size() * sizeof(ge_p3);
+ return sz;
+}
+
+rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<straus_cached_data> &cache)
+{
+ MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000));
+ bool HiGi = cache != NULL;
+
+ MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000));
+ static constexpr unsigned int mask = (1<<STRAUS_C)-1;
+ std::shared_ptr<straus_cached_data> local_cache = cache == NULL ? straus_init_cache(data) : cache;
+ ge_cached cached;
+ ge_p1p1 p1;
+ ge_p3 p3;
+
+ std::vector<uint8_t> skip(data.size());
+ for (size_t i = 0; i < data.size(); ++i)
+ skip[i] = data[i].scalar == rct::zero() || !memcmp(&data[i].point, &ge_p3_identity, sizeof(ge_p3));
+
MULTIEXP_PERF(PERF_TIMER_START_UNIT(digits, 1000000));
std::vector<std::vector<uint8_t>> digits;
digits.resize(data.size());
@@ -305,7 +329,7 @@ rct::key straus(const std::vector<MultiexpData> &data, bool HiGi)
memcpy(bytes33, data[j].scalar.bytes, 32);
bytes33[32] = 0;
#if 1
- static_assert(c == 4, "optimized version needs c == 4");
+ static_assert(STRAUS_C == 4, "optimized version needs STRAUS_C == 4");
const unsigned char *bytes = bytes33;
unsigned int i;
for (i = 0; i < 256; i += 8, bytes++)
@@ -339,22 +363,22 @@ rct::key straus(const std::vector<MultiexpData> &data, bool HiGi)
maxscalar = data[i].scalar;
size_t i = 0;
while (i < 256 && !(maxscalar < pow2(i)))
- i += c;
+ i += STRAUS_C;
MULTIEXP_PERF(PERF_TIMER_STOP(setup));
ge_p3 res_p3 = ge_p3_identity;
- if (!(i < c))
+ if (!(i < STRAUS_C))
goto skipfirst;
- while (!(i < c))
+ while (!(i < STRAUS_C))
{
- for (size_t j = 0; j < c; ++j)
+ for (size_t j = 0; j < STRAUS_C; ++j)
{
ge_p3_to_cached(&cached, &res_p3);
ge_add(&p1, &res_p3, &cached);
ge_p1p1_to_p3(&res_p3, &p1);
}
skipfirst:
- i -= c;
+ i -= STRAUS_C;
for (size_t j = 0; j < data.size(); ++j)
{
if (skip[j])
@@ -362,7 +386,7 @@ skipfirst:
int digit = digits[j][i];
if (digit)
{
- ge_add(&p1, &res_p3, &multiples[digit][j]);
+ ge_add(&p1, &res_p3, &local_cache->multiples[digit][j]);
ge_p1p1_to_p3(&res_p3, &p1);
}
}
diff --git a/src/ringct/multiexp.h b/src/ringct/multiexp.h
index cc53e633e..44998e2e0 100644
--- a/src/ringct/multiexp.h
+++ b/src/ringct/multiexp.h
@@ -36,6 +36,7 @@
#include <vector>
#include "crypto/crypto.h"
#include "rctTypes.h"
+#include "misc_log_ex.h"
namespace rct
{
@@ -52,9 +53,13 @@ struct MultiexpData {
}
};
+struct straus_cached_data;
+
rct::key bos_coster_heap_conv(std::vector<MultiexpData> data);
rct::key bos_coster_heap_conv_robust(std::vector<MultiexpData> data);
-rct::key straus(const std::vector<MultiexpData> &data, bool HiGi = false);
+std::shared_ptr<straus_cached_data> straus_init_cache(const std::vector<MultiexpData> &data);
+size_t straus_get_cache_size(const std::shared_ptr<straus_cached_data> &cache);
+rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<straus_cached_data> &cache = NULL);
}
diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp
index c18a653c8..a00f05ce7 100644
--- a/tests/performance_tests/main.cpp
+++ b/tests/performance_tests/main.cpp
@@ -221,6 +221,13 @@ int main(int argc, char** argv)
TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 1024);
TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 4096);
+ TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 2);
+ TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 8);
+ TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 16);
+ TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 256);
+ TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 1024);
+ TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 4096);
+
std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl;
return 0;
diff --git a/tests/performance_tests/multiexp.h b/tests/performance_tests/multiexp.h
index ac5f60fdf..ab5af166b 100644
--- a/tests/performance_tests/multiexp.h
+++ b/tests/performance_tests/multiexp.h
@@ -38,6 +38,7 @@ enum test_multiexp_algorithm
{
multiexp_bos_coster,
multiexp_straus,
+ multiexp_straus_cached,
};
template<test_multiexp_algorithm algorithm, size_t npoints>
@@ -59,6 +60,7 @@ public:
rct::key kn = rct::scalarmultKey(point, data[n].scalar);
res = rct::addKeys(res, kn);
}
+ cache = rct::straus_init_cache(data);
return true;
}
@@ -69,7 +71,9 @@ public:
case multiexp_bos_coster:
return res == bos_coster_heap_conv_robust(data);
case multiexp_straus:
- return res == straus(data, false);
+ return res == straus(data);
+ case multiexp_straus_cached:
+ return res == straus(data, cache);
default:
return false;
}
@@ -77,5 +81,6 @@ public:
private:
std::vector<rct::MultiexpData> data;
+ std::shared_ptr<rct::straus_cached_data> cache;
rct::key res;
};
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index 7366990ad..cdb741699 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -58,6 +58,7 @@ set(unit_tests_sources
mlocker.cpp
mnemonics.cpp
mul_div.cpp
+ multiexp.cpp
multisig.cpp
parse_amount.cpp
random.cpp
diff --git a/tests/unit_tests/multiexp.cpp b/tests/unit_tests/multiexp.cpp
new file mode 100644
index 000000000..2dce5bb80
--- /dev/null
+++ b/tests/unit_tests/multiexp.cpp
@@ -0,0 +1,149 @@
+// Copyright (c) 2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "gtest/gtest.h"
+
+#include "crypto/crypto.h"
+#include "ringct/rctOps.h"
+#include "ringct/multiexp.h"
+
+static const rct::key TESTSCALAR = rct::H;
+static const rct::key TESTPOINT = rct::scalarmultBase(rct::H);
+
+static rct::key basic(const std::vector<rct::MultiexpData> &data)
+{
+ ge_p3 res_p3 = ge_p3_identity;
+ for (const auto &d: data)
+ {
+ ge_cached cached;
+ ge_p3 p3;
+ ge_p1p1 p1;
+ ge_scalarmult_p3(&p3, d.scalar.bytes, &d.point);
+ ge_p3_to_cached(&cached, &p3);
+ ge_add(&p1, &res_p3, &cached);
+ ge_p1p1_to_p3(&res_p3, &p1);
+ }
+ rct::key res;
+ ge_p3_tobytes(res.bytes, &res_p3);
+ return res;
+}
+
+static ge_p3 get_p3(const rct::key &point)
+{
+ ge_p3 p3;
+ EXPECT_TRUE(ge_frombytes_vartime(&p3, point.bytes) == 0);
+ return p3;
+}
+
+TEST(multiexp, bos_coster_empty)
+{
+ std::vector<rct::MultiexpData> data;
+ data.push_back({rct::zero(), get_p3(rct::identity())});
+ ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data));
+}
+
+TEST(multiexp, straus_empty)
+{
+ std::vector<rct::MultiexpData> data;
+ data.push_back({rct::zero(), get_p3(rct::identity())});
+ ASSERT_TRUE(basic(data) == straus(data));
+}
+
+TEST(multiexp, bos_coster_only_zeroes)
+{
+ std::vector<rct::MultiexpData> data;
+ for (int n = 0; n < 16; ++n)
+ data.push_back({rct::zero(), get_p3(TESTPOINT)});
+ ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data));
+}
+
+TEST(multiexp, straus_only_zeroes)
+{
+ std::vector<rct::MultiexpData> data;
+ for (int n = 0; n < 16; ++n)
+ data.push_back({rct::zero(), get_p3(TESTPOINT)});
+ ASSERT_TRUE(basic(data) == straus(data));
+}
+
+TEST(multiexp, bos_coster_only_identities)
+{
+ std::vector<rct::MultiexpData> data;
+ for (int n = 0; n < 16; ++n)
+ data.push_back({TESTSCALAR, get_p3(rct::identity())});
+ ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data));
+}
+
+TEST(multiexp, straus_only_identities)
+{
+ std::vector<rct::MultiexpData> data;
+ for (int n = 0; n < 16; ++n)
+ data.push_back({TESTSCALAR, get_p3(rct::identity())});
+ ASSERT_TRUE(basic(data) == straus(data));
+}
+
+TEST(multiexp, bos_coster_random)
+{
+ std::vector<rct::MultiexpData> data;
+ for (int n = 0; n < 32; ++n)
+ {
+ data.push_back({rct::skGen(), get_p3(rct::scalarmultBase(rct::skGen()))});
+ ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data));
+ }
+}
+
+TEST(multiexp, straus_random)
+{
+ std::vector<rct::MultiexpData> data;
+ for (int n = 0; n < 32; ++n)
+ {
+ data.push_back({rct::skGen(), get_p3(rct::scalarmultBase(rct::skGen()))});
+ ASSERT_TRUE(basic(data) == straus(data));
+ }
+}
+
+TEST(multiexp, straus_cached)
+{
+ static constexpr size_t N = 256;
+ std::vector<rct::MultiexpData> P(N);
+ for (size_t n = 0; n < N; ++n)
+ {
+ P[n].scalar = rct::zero();
+ ASSERT_TRUE(ge_frombytes_vartime(&P[n].point, rct::scalarmultBase(rct::skGen()).bytes) == 0);
+ }
+ std::shared_ptr<rct::straus_cached_data> cache = rct::straus_init_cache(P);
+ for (size_t n = 0; n < N/16; ++n)
+ {
+ std::vector<rct::MultiexpData> data;
+ size_t sz = 1 + crypto::rand<size_t>() % (N-1);
+ for (size_t s = 0; s < sz; ++s)
+ {
+ data.push_back({rct::skGen(), P[s].point});
+ }
+ ASSERT_TRUE(basic(data) == straus(data, cache));
+ }
+}