Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
/build
/Testing
/cmake-build-debug
/build-bench
.vscode
9 changes: 8 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,11 @@ set(CMAKE_CXX_EXTENSIONS OFF)
add_subdirectory(src)

include(CTest)
add_subdirectory(tests)
add_subdirectory(tests)

# Benchmarks are disabled by default
option(BIGINT_BUILD_BENCHMARKS "Build BigInt benchmarks" ON)

if(BIGINT_BUILD_BENCHMARKS)
add_subdirectory(benchmarks)
endif()
26 changes: 26 additions & 0 deletions benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
include(FetchContent)

# Disable Google Benchmark's own tests
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)

FetchContent_Declare(
googlebenchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG v1.9.5
GIT_SHALLOW TRUE
)

FetchContent_MakeAvailable(googlebenchmark)

add_executable(BigInt-Benchmarks
benchmark_bigint.cpp
)

target_link_libraries(
BigInt-Benchmarks
PRIVATE
BigInt
benchmark::benchmark_main
)
184 changes: 184 additions & 0 deletions benchmarks/benchmark_bigint.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#include <benchmark/benchmark.h>

#include "bigint.h"
#include "math_utils.h"

#include <cstddef>
#include <cstdint>

using operations::BigInt;

namespace {

// Erstellt einen BigInt mit ungefähr/exakt der gewünschten Limb-Größe.
// Da ein Limb uint64_t ist:
// 2048 Bit = 32 Limbs.
BigInt makeBigInt(const std::size_t bits, const std::uint64_t pattern) {
const std::size_t limbCount = (bits + 63) / 64;

ByteArray limbs(limbCount, pattern);

return BigInt(std::move(limbs));
}


// ------------------------------------------------------------
// Addition
// ------------------------------------------------------------

static void BM_Add(benchmark::State& state) {
const auto bits = static_cast<std::size_t>(state.range(0));

const BigInt a = makeBigInt(bits, 0xAAAAAAAAAAAAAAAAULL);
const BigInt b = makeBigInt(bits, 0x5555555555555555ULL);

for (auto _ : state) {
BigInt result = a + b;

benchmark::DoNotOptimize(result);
}

state.SetComplexityN(static_cast<benchmark::IterationCount>(bits / 64));
}

BENCHMARK(BM_Add)
->RangeMultiplier(2)
->Range(64, 8192)
->Complexity();


// ------------------------------------------------------------
// Subtraction
// ------------------------------------------------------------

static void BM_Subtract(benchmark::State& state) {
const auto bits = static_cast<std::size_t>(state.range(0));

// a > b, wichtig bei unsigned BigInt
const BigInt a = makeBigInt(bits, 0xFFFFFFFFFFFFFFFFULL);
const BigInt b = makeBigInt(bits, 0x5555555555555555ULL);

for (auto _ : state) {
BigInt result = a - b;

benchmark::DoNotOptimize(result);
}

state.SetComplexityN(static_cast<benchmark::IterationCount>(bits / 64));
}

BENCHMARK(BM_Subtract)
->RangeMultiplier(2)
->Range(64, 8192)
->Complexity();


// ------------------------------------------------------------
// Multiplikation
// ------------------------------------------------------------

static void BM_Multiply(benchmark::State& state) {
const auto bits = static_cast<std::size_t>(state.range(0));

const BigInt a = makeBigInt(bits, 0xAAAAAAAAAAAAAAAAULL);
const BigInt b = makeBigInt(bits, 0x5555555555555555ULL);

for (auto _ : state) {
BigInt result = a * b;

benchmark::DoNotOptimize(result);
}

state.SetComplexityN(static_cast<benchmark::IterationCount>(bits / 64));
}

BENCHMARK(BM_Multiply)
->RangeMultiplier(2)
->Range(64, 8192)
->Complexity();


// ------------------------------------------------------------
// Division
// ------------------------------------------------------------

static void BM_Divide(benchmark::State& state) {
const auto bits = static_cast<std::size_t>(state.range(0));

// a > b
const BigInt a = makeBigInt(bits, 0xFFFFFFFFFFFFFFFFULL);
const BigInt b = makeBigInt(bits, 0x5555555555555555ULL);

for (auto _ : state) {
BigInt result = a / b;

benchmark::DoNotOptimize(result);
}

state.SetComplexityN(static_cast<benchmark::IterationCount>(bits / 64));
}

BENCHMARK(BM_Divide)
->RangeMultiplier(2)
->Range(64, 4096)
->Complexity();


// ------------------------------------------------------------
// Modulo
// ------------------------------------------------------------

static void BM_Modulo(benchmark::State& state) {
const auto bits = static_cast<std::size_t>(state.range(0));

const BigInt a = makeBigInt(bits, 0xFFFFFFFFFFFFFFFFULL);
const BigInt b = makeBigInt(bits, 0x5555555555555555ULL);

for (auto _ : state) {
BigInt result = a % b;

benchmark::DoNotOptimize(result);
}

state.SetComplexityN(static_cast<benchmark::IterationCount>(bits / 64));
}

BENCHMARK(BM_Modulo)
->RangeMultiplier(2)
->Range(64, 4096)
->Complexity();


// ------------------------------------------------------------
// Modular Exponentiation
//
// Verwendet exponent = 65537.
// Das ist für einen ersten Benchmark wesentlich sinnvoller als
// direkt einen mehrere tausend Bit großen Exponenten zu verwenden.
// ------------------------------------------------------------

static void BM_ModPow65537(benchmark::State& state) {
const auto bits = static_cast<std::size_t>(state.range(0));

const BigInt base =
makeBigInt(bits, 0x5555555555555555ULL);

const BigInt exponent(65537);

// modulus > base
const BigInt modulus =
makeBigInt(bits, 0xFFFFFFFFFFFFFFFFULL);

for (auto _ : state) {
BigInt result =
operations::math::modPow(base, exponent, modulus);

benchmark::DoNotOptimize(result);
}
}

BENCHMARK(BM_ModPow65537)
->RangeMultiplier(2)
->Range(256, 2048);

} // namespace
50 changes: 25 additions & 25 deletions src/bigint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,58 +133,58 @@ void operations::BigInt::mul(const ByteArray &b) noexcept {

for (size_t i = 0; i < size_a; ++i) {
std::uint64_t carry = 0;
std::uint64_t valA = data[i];
const std::uint64_t valA = data[i];

for (size_t j = 0; j < size_b; ++j) {
std::uint64_t valB = b[j];
const std::uint64_t n1 = valA >> 32;
const std::uint64_t n2 = valA & 0xFFFFFFFFULL;

std::uint64_t n1 = valA >> 32;
std::uint64_t n2 = valA & 0xFFFFFFFFULL;
for (size_t j = 0; j < size_b; ++j) {
const std::uint64_t valB = b[j];

std::uint64_t n3 = valB >> 32;
std::uint64_t n4 = valB & 0xFFFFFFFFULL;
const std::uint64_t n3 = valB >> 32;
const std::uint64_t n4 = valB & 0xFFFFFFFFULL;

std::uint64_t r1 = n1 * n3;
const std::uint64_t r1 = n1 * n3;
std::uint64_t r2 = n2 * n4;
std::uint64_t r3 = n1 * n4;
std::uint64_t r4 = n2 * n3;
const std::uint64_t r3 = n1 * n4;
const std::uint64_t r4 = n2 * n3;

std::uint64_t c1 = r1 >> 32;
std::uint64_t c2 = r3 >> 32;
std::uint64_t fr1 = (r1 << 32) | (r3 & 0xFFFFFFFFULL);
std::uint64_t fc1 = (c1 << 32) | c2;
const std::uint64_t c1 = r1 >> 32;
const std::uint64_t c2 = r3 >> 32;
const std::uint64_t fr1 = (r1 << 32) | (r3 & 0xFFFFFFFFULL);
const std::uint64_t fc1 = (c1 << 32) | c2;

std::uint64_t c3 = r2 >> 32;
const std::uint64_t c3 = r2 >> 32;
r2 &= 0xFFFFFFFFULL;
std::uint64_t c4 = r4 >> 32;
std::uint64_t fr2 = (r4 << 32) | r2;
std::uint64_t fc2 = c3 | (c4 << 32);
const std::uint64_t c4 = r4 >> 32;
const std::uint64_t fr2 = (r4 << 32) | r2;
const std::uint64_t fc2 = c3 | (c4 << 32);

std::uint64_t fl = fr2;
std::uint64_t runningCarry = 0;
std::uint64_t valFL = fc2 << 32;
const std::uint64_t valFL = fc2 << 32;
fl += valFL;
if (valFL > fl) {
runningCarry += 1ULL;
}

std::uint64_t valFH = fr1 << 32;
const std::uint64_t valFH = fr1 << 32;
fl += valFH;
if (valFH > fl) {
runningCarry += 1ULL;
}

std::uint64_t fh = (fc2 >> 32) + (fr1 >> 32) + fc1 + runningCarry;
const std::uint64_t fh = (fc2 >> 32) + (fr1 >> 32) + fc1 + runningCarry;


std::uint64_t current = result[i + j];
const std::uint64_t current = result[i + j];

std::uint64_t sum_low = current + fl;
std::uint64_t carry_out1 = (sum_low < current) ? 1ULL : 0ULL;
const std::uint64_t carry_out1 = (sum_low < current) ? 1ULL : 0ULL;

std::uint64_t sum_low_prev = sum_low;
const std::uint64_t sum_low_prev = sum_low;
sum_low += carry;
std::uint64_t carry_out2 = (sum_low < sum_low_prev) ? 1ULL : 0ULL;
const std::uint64_t carry_out2 = (sum_low < sum_low_prev) ? 1ULL : 0ULL;

result[i + j] = sum_low;

Expand Down
Loading