Skip to content

Commit

Permalink
Switch to google's benchmarking (#18)
Browse files Browse the repository at this point in the history
Small refactoring. Updated sanitizing functionality. Improved code for sanitizing. Crucial improvements in functionality presentation + its testing. Improvements in execution time measures. Resolved issues in pipeline. Improvements in pipeline. Created workflow for testing as library.
  • Loading branch information
Alvov1 authored Nov 27, 2024
1 parent 85961fc commit 13aa15c
Show file tree
Hide file tree
Showing 29 changed files with 837 additions and 408 deletions.
71 changes: 71 additions & 0 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Benchmarking

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
Measure:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install packages
run: sudo apt-get -qq install libbenchmark-dev libgmp3-dev libcrypto++-dev libcrypto++-doc libcrypto++-utils

- name: Set reusable strings
id: strings
shell: bash
run: |
echo "ci=${{ github.workspace }}/ci/benchmarking" >> "$GITHUB_OUTPUT"
echo "benchmark=${{ github.workspace }}/benchmark" >> "$GITHUB_OUTPUT"
echo "build=${{ github.workspace }}/benchmark/cmake-build-release" >> "$GITHUB_OUTPUT"
- name: Load CMake configuration
run: >
cmake -B ${{ steps.strings.outputs.build }}
-DCMAKE_CXX_COMPILER=g++
-DCMAKE_C_COMPILER=gcc
-DCMAKE_BUILD_TYPE=Release
-S ${{ steps.strings.outputs.benchmark }}
- name: Build executable
run: cmake --build ${{ steps.strings.outputs.build }} --target Benchmarking -j8

- name: Run benchmark analysis
working-directory: ${{ steps.strings.outputs.build }}
run: ./Benchmarking --benchmark_out=benchmarks.json

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Install python packages
run: |
python -m pip install -q --upgrade pip
pip install -q matplotlib numpy requests
- name: Download font file
working-directory: ${{ steps.strings.outputs.ci }}
run: wget https://github.com/google/fonts/raw/refs/heads/main/ofl/courierprime/CourierPrime-Bold.ttf -q

- name: Run Python script
working-directory: ${{ steps.strings.outputs.ci }}
run: python make_plot.py ${{ steps.strings.outputs.build }}/benchmarks.json graph.png CourierPrime-Bold.ttf

- name: Upload a picture
uses: devicons/[email protected]
id: imgur_step
with:
path: ${{ steps.strings.outputs.ci }}/graph.png
client_id: ${{ secrets.IMGUR_CLIENT_ID }}

- name: Update image link
working-directory: ${{ steps.strings.outputs.ci }}
run: python update_image_link.py ${{ secrets.DUB_CO_LINK_ID }} ${{ fromJSON(steps.imgur_step.outputs.imgur_urls)[0] }} ${{ secrets.DUB_CO_API_KEY }}


38 changes: 38 additions & 0 deletions .github/workflows/build_multiple_platforms.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Build as library

on:
push:
branches: [ "main" ]

jobs:
Test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set reusable strings
id: strings
shell: bash
run: |
echo "build=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
- name: Create directory
run: mkdir ${{ steps.strings.outputs.build }}

- name: Load CMake configuration
working-directory: ${{ steps.strings.outputs.build }}
run: cmake -B ${{ steps.strings.outputs.build }}
-DCMAKE_CXX_COMPILER=g++
-DCMAKE_C_COMPILER=gcc
-DCMAKE_BUILD_TYPE=Release
-S ${{ github.workspace }}

- name: Test
working-directory: ${{ steps.strings.outputs.build }}
run: |
make -j8
./Build
67 changes: 0 additions & 67 deletions .github/workflows/generate_time_table.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/gtest_multiple_platforms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@v4

- name: Install GTest
run: sudo apt-get install libgtest-dev
run: sudo apt-get -qq install libgtest-dev

- name: Set reusable strings
id: strings
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/sanitize_multiple_platforms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: [ "main" ]

jobs:
Sanitize:
Run:
runs-on: ${{ matrix.os }}

strategy:
Expand Down Expand Up @@ -50,4 +50,4 @@ jobs:

- name: Run sanitizing application
working-directory: ${{ steps.strings.outputs.build-output-dir }}
run: /${{ steps.strings.outputs.build-output-dir }}/AesiSanitize
run: /${{ steps.strings.outputs.build-output-dir }}/AesiSanitize ../../primes.bin
34 changes: 19 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,33 +88,37 @@ This project is licensed under the BSD 2-Clause License. See the LICENSE file fo
It is admissible to use numbers of different precision inside the majority of operations, but it is not recommended cause it leads to redundant copying inside type conversions. Operation-assignment expressions (+=, -=, &=, etc...) require the bitness of the assignment to be greater or equal to the bitness of the assignable. The precision cast operator could be called by a user directly.

```cpp
Aesi<128> base = "10888869450418352160768000001";
Aesi<96> power = "99990001";
Aesi<256> mod = "8683317618811886495518194401279999999";
Aeu<128> base = "10888869450418352160768000001";
Aeu<96> power = "99990001";
Aeu<256> mod = "8683317618811886495518194401279999999";

std::cout << Aesi<256>::powm(base, power, mod) << std::endl;
// Numbers get cast explicitly to bitness 256
cout << Aeu<256>::powm(base.precisionCast<256>(), power.precisionCast<256>(), mod) << endl;

Aesi<128> m128 = "265252859812191058636308479999999";
Aesi<160> m160 = "263130836933693530167218012159999999";
Aeu<128> m128 = "127958277599458332250117";
Aeu<192> m192 = "279256103987149586783914830";

std::cout << m128.precisionCast<256>() * m160 << std::endl;
cout << m128.precisionCast<192>() * m192 << endl;
// Cast number of 128 bits to 256 bits, than multiply by number of 160 bits
```
> 1142184225164688919052733263067509431086585217025 6680141832773294447513292887050873529
> 6680141832773294447513292887050873529 35733130075330889632933652650476631619495985535110
An exception to the rule above is using longer precision boundaries inside functions, susceptible to overflow. As far as the number's precision is fixed on the stage of compilation, functions that require number multiplication or exponentiation may easily lead to overflow:
```cpp
Aesi<128> base = "340199290171201906239764863559915798527",
Aeu<128> base = "340199290171201906239764863559915798527",
power = "340282366920937859000464800151540596704",
modulo = "338953138925230918806032648491249958912";

std::cout << Aesi<128>::powm(base, power, modulo) << std::endl; // Overflow !!!
std::cout << Aesi<256>::powm(base, power, modulo) << std::endl; // Fine
cout << Aeu<128>::powm(base, power, modulo) << endl; // Overflow !!!

cout << Aeu<256>::powm(base.precisionCast<256>(), // Fine
power,
modulo.precisionCast<256>()) << endl;
```
> 201007033690655614485250957754150944769
> \***Overflowed\*** 201007033690655614485250957754150944769
## Issues
Library is relatively slow in compare to other multiple precision libraries
<img src="https://dub.sh/jNgf79u" alt="Dynamic Image">
Library is relatively slow in comparison to popular CPU-directional multiple precision libraries:


![Execution Time Graph](https://dub.sh/jNgf79u?2)

25 changes: 25 additions & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.26)
set(CMAKE_CXX_STANDARD 20)
project(Benchmarking)

find_package(PkgConfig)
pkg_check_modules(GMP REQUIRED IMPORTED_TARGET gmp)

find_path(BENCH_INCLUDE benchmark/benchmark.h /usr/include /usr/local/include /opt/homebrew/include)
find_library(BENCH_LIB benchmark /usr/lib /usr/local/lib /opt/homebrew/lib)
if(NOT BENCH_INCLUDE OR NOT BENCH_LIB)
message(FATAL_ERROR "Google Benchmarking is not found!")
endif()

find_path(CRYPTOPP_INCLUDE cryptopp/cryptlib.h /usr/include /usr/local/include /opt/homebrew/include)
find_library(CRYPTOPP_LIB cryptopp /usr/lib /usr/local/lib /opt/homebrew/lib)
if(NOT CRYPTOPP_INCLUDE OR NOT CRYPTOPP_LIB)
message(FATAL_ERROR "Crypto++ is not found!")
endif()

file(GLOB Benches *.cpp)
add_executable(Benchmarking ${Benches})

include_directories(${CRYPTOPP_INCLUDE} ${BENCH_INCLUDE})
target_link_libraries(Benchmarking ${BENCH_LIB} ${CRYPTOPP_LIB} PkgConfig::GMP)
target_include_directories(Benchmarking PUBLIC PkgConfig::GMP)
29 changes: 29 additions & 0 deletions benchmark/addition.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <benchmark/benchmark.h>
#include <cryptopp/integer.h>
#include <gmpxx.h>
#include "../Aeu.h"

constexpr char data[] = "0x56612239db6d8ce375c48f335a4ba6f4933c871a672f6e66c7899af62393b55fb0fd38984923f6c7eb2d5f97b66a"
"c90bedaf1978972ec071c899f05d006caa686401d48c670c3c49553c15c3b7053eddc1878132dfce005cb4d8151fee"
"333b98656b4fc831c569bf7909f929ee6b6f693df50e2c049643195e2f648d593fb543";

static void addition_CryptoPP(benchmark::State& state) {
CryptoPP::Integer left (data), right (data), result {};
for (auto _ : state)
benchmark::DoNotOptimize(result = left + right);
}
BENCHMARK(addition_CryptoPP);

static void addition_GMP(benchmark::State& state) {
mpz_class left (data), right (data), result {};
for (auto _ : state)
benchmark::DoNotOptimize(result = left + right);
}
BENCHMARK(addition_GMP);

static void addition_Aesi(benchmark::State& state) {
Aeu<2048> left (data), right (data), result {};
for (auto _ : state)
benchmark::DoNotOptimize(result = left + right);
}
BENCHMARK(addition_Aesi);
41 changes: 17 additions & 24 deletions ci/speed_comparison/division.cpp → benchmark/division.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#include <gtest/gtest.h>
#include <thread>
#include <benchmark/benchmark.h>
#include <cryptopp/integer.h>
#include <gmpxx.h>
#include "../../Aeu.h"
#include "../Aeu.h"

constexpr char division[] = "0x1099091f922d948121cf94880af1fd07a60010c9bbf89884aac215f37c6418b2735a3e50e0889fac0c3ea61d"
"bc829d3919e94bf714f521969e75e15f570f870ef5e086add27842cfc8cafd321d038354a97e152c0ea74df004"
Expand Down Expand Up @@ -30,29 +29,23 @@ constexpr char division[] = "0x1099091f922d948121cf94880af1fd07a60010c9bbf89884a

constexpr char divisor[] = "0x55c5374ad14e5c9bff62109df3100124f654bb11ef8fbdcc93e892fde002a462";

TEST(Division, CryptoPP) {
for(std::size_t i = 0; i < 40; ++i) {
CryptoPP::Integer base (division), oper (divisor);
for (std::size_t j = 0; j < 30; ++j)
base /= oper;
if(base.IsZero()) std::cout << '1';
}
static void division_CryptoPP(benchmark::State& state) {
CryptoPP::Integer left (division), right (divisor), result {};
for(auto _ : state)
benchmark::DoNotOptimize(result = left / right);
}
BENCHMARK(division_CryptoPP);

TEST(Division, GMP) {
for(std::size_t i = 0; i < 40; ++i) {
mpz_class base (division), oper (divisor);
for (std::size_t j = 0; j < 30; ++j)
base /= oper;
if(base == 0) std::cout << '1';
}
static void division_GMP(benchmark::State& state) {
mpz_class left (division), right (divisor), result {};
for(auto _ : state)
benchmark::DoNotOptimize(result = left / right);
}
BENCHMARK(division_GMP);

TEST(Division, Aesi) {
for(std::size_t i = 0; i < 40; ++i) {
Aeu<8192> base = division, oper = divisor;
for (std::size_t j = 0; j < 30; ++j)
base /= oper;
if(base.isZero()) std::cout << '1';
}
static void division_Aesi(benchmark::State& state) {
Aeu<8192> left = division, right = divisor, result {};
for(auto _ : state)
benchmark::DoNotOptimize(result = left / right);
}
BENCHMARK(division_Aesi);
Loading

0 comments on commit 13aa15c

Please sign in to comment.