-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #507 from cppalliance/benchmarks
Add Benchmarks
- Loading branch information
Showing
4 changed files
with
310 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
//// | ||
Copyright 2024 Matt Borland | ||
Distributed under the Boost Software License, Version 1.0. | ||
https://www.boost.org/LICENSE_1_0.txt | ||
//// | ||
|
||
[#Benchmarks] | ||
= Benchmarks | ||
:idprefix: benchmarks_ | ||
|
||
This section describes a range of performance benchmarks that have been run comparing this library with the standard library, and how to run your own benchmarks if required. | ||
|
||
The values in the ratio column are how many times longer running a specific operation takes in comparison to the same operation with a `double`. | ||
|
||
IMPORTANT: On nearly all platforms there is hardware support for binary floating point math, so we are comparing hardware to software runtimes; *Decimal will be slower* | ||
|
||
== How to run the Benchmarks | ||
[#run_benchmarks_] | ||
|
||
To run the benchmarks yourself, navigate to the test folder and define `BOOST_DECIMAL_RUN_BENCHMARKS` when running the tests. | ||
An example on Linux with b2: `../../../b2 cxxstd=20 toolset=gcc-13 define=BOOST_DECIMAL_RUN_BENCHMARKS benchmarks -a release` . | ||
|
||
== Comparisons | ||
|
||
The benchmark for comparisons generates a random vector containing 10,000,000 elements and does operations `>`, `>=`, `<`, `<=`, `==`, and `!=` between `vec[i] and vec[i + 1]`. | ||
|
||
=== M1 macOS Results | ||
|
||
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 | ||
|
||
|=== | ||
| Type | Runtime (ms) | Ratio to `double` | ||
| `float` | ||
| 8 | ||
| 1.333 | ||
| `double` | ||
| 6 | ||
| 1.000 | ||
| `decimal32` | ||
| 380 | ||
| 63.333 | ||
| `decimal64` | ||
| 408 | ||
| 608.000 | ||
| `decimal128` | ||
| 14641 | ||
| 2440.170 | ||
|=== | ||
|
||
== Basic Operations | ||
|
||
The benchmark for these operations generates a random vector containing 10,000,000 elements and does operations `+`, `-`, `*`, `/` between `vec[i] and vec[i + 1]`. | ||
|
||
=== M1 macOS Results | ||
|
||
Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 | ||
|
||
==== Addition | ||
|
||
|=== | ||
| Type | Runtime (ms) | Ratio to `double` | ||
| `float` | ||
| 6 | ||
| 1.500 | ||
| `double` | ||
| 4 | ||
| 1.000 | ||
| `decimal32` | ||
| 361 | ||
| 90.250 | ||
| `decimal64` | ||
| 568 | ||
| 142.000 | ||
| `decimal128` | ||
| 13428 | ||
| 3357.000 | ||
|=== | ||
|
||
==== Subtraction | ||
|
||
|=== | ||
| Type | Runtime (ms) | Ratio to `double` | ||
| `float` | ||
| 3 | ||
| 3.000 | ||
| `double` | ||
| 1 | ||
| 1.000 | ||
| `decimal32` | ||
| 307 | ||
| 307.000 | ||
| `decimal64` | ||
| 465 | ||
| 465.000 | ||
| `decimal128` | ||
| 11444 | ||
| 11444.000 | ||
|=== | ||
|
||
==== Multiplication | ||
|
||
|=== | ||
| Type | Runtime (ms) | Ratio to `double` | ||
| `float` | ||
| 1 | ||
| 0.333 | ||
| `double` | ||
| 3 | ||
| 1.000 | ||
| `decimal32` | ||
| 311 | ||
| 103.667 | ||
| `decimal64` | ||
| 569 | ||
| 189.667 | ||
| `decimal128` | ||
| 9430 | ||
| 3143.330 | ||
|=== | ||
|
||
==== Division | ||
|
||
|=== | ||
| Type | Runtime (ms) | Ratio to `double` | ||
| `float` | ||
| 2 | ||
| 0.667 | ||
| `double` | ||
| 3 | ||
| 1.000 | ||
| `decimal32` | ||
| 319 | ||
| 106.333 | ||
| `decimal64` | ||
| 395 | ||
| 131.667 | ||
| `decimal128` | ||
| 14781 | ||
| 4927.000 | ||
|=== |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// Copyright 2024 Matt Borland | ||
// Distributed under the Boost Software License, Version 1.0. | ||
// https://www.boost.org/LICENSE_1_0.txt | ||
|
||
#include <boost/decimal.hpp> | ||
#include <chrono> | ||
#include <random> | ||
#include <vector> | ||
#include <type_traits> | ||
#include <iostream> | ||
#include <iomanip> | ||
|
||
#ifdef BOOST_DECIMAL_RUN_BENCHMARKS | ||
|
||
using namespace boost::decimal; | ||
using namespace std::chrono_literals; | ||
|
||
#ifdef __clang__ | ||
# pragma clang diagnostic push | ||
# pragma clang diagnostic ignored "-Wfloat-equal" | ||
# define BOOST_DECIMAL_NO_INLINE __attribute__ ((__noinline__)) | ||
#elif defined(_MSC_VER) | ||
# define BOOST_DECIMAL_NO_INLINE __declspec(noinline) | ||
#endif | ||
|
||
template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true> | ||
std::vector<T> generate_random_vector(std::size_t size = 10'000'000U, unsigned seed = 42U) | ||
{ | ||
if (seed == 0) | ||
{ | ||
std::random_device rd; | ||
seed = rd(); | ||
} | ||
std::vector<T> v(size); | ||
|
||
std::mt19937_64 gen(seed); | ||
|
||
std::uniform_real_distribution<T> dis(0, 1); | ||
for (std::size_t i = 0; i < v.size(); ++i) | ||
{ | ||
v[i] = dis(gen); | ||
} | ||
return v; | ||
} | ||
|
||
template <typename T, std::enable_if_t<!std::is_floating_point<T>::value, bool> = true> | ||
std::vector<T> generate_random_vector(std::size_t size = 10'000'000U, unsigned seed = 42U) | ||
{ | ||
if (seed == 0) | ||
{ | ||
std::random_device rd; | ||
seed = rd(); | ||
} | ||
std::vector<T> v(size); | ||
|
||
std::mt19937_64 gen(seed); | ||
|
||
std::uniform_real_distribution<double> dis(0, 1); | ||
for (std::size_t i = 0; i < v.size(); ++i) | ||
{ | ||
v[i] = T{dis(gen)}; | ||
} | ||
return v; | ||
} | ||
|
||
template <typename T> | ||
BOOST_DECIMAL_NO_INLINE void test_comparisons(const std::vector<T>& data_vec, const char* label) | ||
{ | ||
const auto t1 = std::chrono::steady_clock::now(); | ||
std::size_t s = 0; // discard variable | ||
|
||
for (std::size_t i {}; i < data_vec.size() - 1U; ++i) | ||
{ | ||
const auto val1 = data_vec[i]; | ||
const auto val2 = data_vec[i + 1]; | ||
s += static_cast<std::size_t>(val1 > val2); | ||
s += static_cast<std::size_t>(val1 >= val2); | ||
s += static_cast<std::size_t>(val1 < val2); | ||
s += static_cast<std::size_t>(val1 <= val2); | ||
s += static_cast<std::size_t>(val1 == val2); | ||
s += static_cast<std::size_t>(val1 != val2); | ||
} | ||
|
||
const auto t2 = std::chrono::steady_clock::now(); | ||
|
||
std::cout << "comparisons<" << std::left << std::setw(10) << label << ">: " << std::setw( 6 ) << ( t2 - t1 ) / 1ms << " ms (s=" << s << ")\n"; | ||
} | ||
|
||
template <typename T, typename Func> | ||
BOOST_DECIMAL_NO_INLINE void test_operation(const std::vector<T>& data_vec, Func op, const char* operation, const char* type) | ||
{ | ||
const auto t1 = std::chrono::steady_clock::now(); | ||
std::size_t s = 0; // discard variable | ||
|
||
for (std::size_t i {}; i < data_vec.size() - 1U; ++i) | ||
{ | ||
const auto val1 = data_vec[i]; | ||
const auto val2 = data_vec[i + 1]; | ||
s += static_cast<std::size_t>(op(val1, val2)); | ||
} | ||
|
||
const auto t2 = std::chrono::steady_clock::now(); | ||
|
||
std::cout << operation << "<" << std::left << std::setw(10) << type << ">: " << std::setw( 6 ) << ( t2 - t1 ) / 1ms << " ms (s=" << s << ")\n"; | ||
} | ||
|
||
int main() | ||
{ | ||
const auto float_vector = generate_random_vector<float>(); | ||
const auto double_vector = generate_random_vector<double>(); | ||
const auto dec32_vector = generate_random_vector<decimal32>(); | ||
const auto dec64_vector = generate_random_vector<decimal64>(); | ||
const auto dec128_vector = generate_random_vector<decimal128>(); | ||
|
||
std::cout << "===== Comparisons =====\n"; | ||
|
||
test_comparisons(float_vector, "float"); | ||
test_comparisons(double_vector, "double"); | ||
test_comparisons(dec32_vector, "decimal32"); | ||
test_comparisons(dec64_vector, "decimal64"); | ||
test_comparisons(dec128_vector, "decimal128"); | ||
|
||
std::cout << "\n===== Addition =====\n"; | ||
|
||
test_operation(float_vector, std::plus<>(), "Addition", "float"); | ||
test_operation(double_vector, std::plus<>(), "Addition", "double"); | ||
test_operation(dec32_vector, std::plus<>(), "Addition", "decimal32"); | ||
test_operation(dec64_vector, std::plus<>(), "Addition", "decimal64"); | ||
test_operation(dec128_vector, std::plus<>(), "Addition", "decimal128"); | ||
|
||
std::cout << "\n===== Subtraction =====\n"; | ||
|
||
test_operation(double_vector, std::minus<>(), "Subtraction", "double"); | ||
test_operation(float_vector, std::minus<>(), "Subtraction", "float"); | ||
test_operation(dec32_vector, std::minus<>(), "Subtraction", "decimal32"); | ||
test_operation(dec64_vector, std::minus<>(), "Subtraction", "decimal64"); | ||
test_operation(dec128_vector, std::minus<>(), "Subtraction", "decimal128"); | ||
|
||
std::cout << "\n===== Multiplication =====\n"; | ||
|
||
test_operation(float_vector, std::multiplies<>(), "Multiplication", "float"); | ||
test_operation(double_vector, std::multiplies<>(), "Multiplication", "double"); | ||
test_operation(dec32_vector, std::multiplies<>(), "Multiplication", "decimal32"); | ||
test_operation(dec64_vector, std::multiplies<>(), "Multiplication", "decimal64"); | ||
test_operation(dec128_vector, std::multiplies<>(), "Multiplication", "decimal128"); | ||
|
||
std::cout << "\n===== Division =====\n"; | ||
|
||
test_operation(float_vector, std::divides<>(), "Division", "float"); | ||
test_operation(double_vector, std::divides<>(), "Division", "double"); | ||
test_operation(dec32_vector, std::divides<>(), "Division", "decimal32"); | ||
test_operation(dec64_vector, std::divides<>(), "Division", "decimal64"); | ||
test_operation(dec128_vector, std::divides<>(), "Division", "decimal128"); | ||
|
||
std::cout << std::endl; | ||
|
||
return 1; | ||
} | ||
|
||
#else | ||
|
||
int main() | ||
{ | ||
std::cout << "Benchmarks not run" << std::endl; | ||
return 1; | ||
} | ||
|
||
#endif |