Compare commits

4 Commits

Author SHA1 Message Date
f0bba4b97c Timings get auto-comitted
Some checks failed
Merge-Checker / build_and_test (pull_request) Failing after 22s
2025-05-21 18:15:15 -04:00
2d8a4ed485 Added matrix test timings
All checks were successful
Merge-Checker / build_and_test (pull_request) Successful in 24s
2025-05-21 18:03:18 -04:00
e179fcc701 Updated merge checker and seperated the matrix tests fro mthe timing tests
All checks were successful
Merge-Checker / build_and_test (pull_request) Successful in 21s
2025-05-21 17:58:18 -04:00
c03e26ef38 Updated readme
All checks were successful
Merge-Checker / build (pull_request) Successful in 1m34s
2025-05-21 17:48:18 -04:00
8 changed files with 200 additions and 219 deletions

View File

@@ -3,24 +3,65 @@ name: Merge-Checker
on: on:
pull_request: pull_request:
branches: ["**"] branches: ["**"]
paths-ignore:
- 'unit-tests/timing-results/**'
jobs: jobs:
build: build_and_test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
persist-credentials: true
fetch-depth: 0
- name: Install dependencies (CMake + Ninja + Compiler) - name: Install dependencies (CMake + Ninja + build tools)
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y cmake ninja-build build-essential sudo apt-get install -y cmake ninja-build build-essential time git
- name: Configure project with CMake - name: Configure project with CMake
run: | run: cmake -G Ninja -S . -B build/
cmake -G Ninja -S . -B build/
- name: Build with Ninja - name: Build with Ninja
run: ninja -C build/
- name: Run all unit tests except matrix-timing-tests
run: | run: |
ninja -C build/ for test_exec in build/unit-tests/matrix-tests build/unit-tests/quaternion-tests build/unit-tests/vector-3d-tests; do
if [ -x "$test_exec" ]; then
echo "Running $test_exec"
"$test_exec"
else
echo "Warning: $test_exec not found or not executable"
fi
done
- name: Run matrix-timing-tests with per-test timing output and save results
run: |
mkdir -p unit-tests/timing-results
if [ -x build/unit-tests/matrix-timing-tests ]; then
echo "Running matrix-timing-tests with timing"
/usr/bin/time -v build/unit-tests/matrix-timing-tests -d yes &> unit-tests/timing-results/matrix-timing-tests.txt
else
echo "matrix-timing-tests executable not found or not executable"
exit 1
fi
- name: Commit and push timing results
if: github.event.pull_request.head.repo.full_name == github.repository # Only push from same repo
run: |
git config --global user.name "ci-bot"
git config --global user.email "ci-bot@local"
BRANCH_NAME="${{ github.event.pull_request.head.ref }}"
git add unit-tests/timing-results/matrix-timing-tests.txt
if git diff --quiet --cached; then
echo "No changes to commit"
else
git commit -m "Update matrix-timing-tests timings [skip ci]"
git push origin HEAD:$BRANCH_NAME
fi

View File

@@ -1 +1,2 @@
# Introduction
This matrix math library is focused on embedded development and avoids any heap memory allocation unless you explicitly ask for it. This matrix math library is focused on embedded development and avoids any heap memory allocation unless you explicitly ask for it.

View File

@@ -16,6 +16,15 @@ target_link_libraries(matrix-tests
Catch2::Catch2WithMain Catch2::Catch2WithMain
) )
# matrix timing tests
add_executable(matrix-timing-tests matrix-timing-tests.cpp)
target_link_libraries(matrix-timing-tests
PRIVATE
matrix
Catch2::Catch2WithMain
)
# Vector 3D Tests # Vector 3D Tests
add_executable(vector-3d-tests vector-tests.cpp) add_executable(vector-3d-tests vector-tests.cpp)

View File

@@ -1,14 +0,0 @@
Addition: 0.419 s
Subtraction: 0.421 s
Multiplication: 3.297 s
Scalar Multiplication: 0.329 s
Element Multiply: 0.306 s
Element Divide: 0.302 s
Minor Matrix: 0.331 s
Determinant: 0.177 s
Matrix of Minors: 0.766 s
Invert: 0.183 s
Transpose: 0.215 s
Normalize: 0.315 s
GET ROW: 0.008 s
GET COLUMN: 0.43 s

View File

@@ -10,15 +10,13 @@
#include <cmath> #include <cmath>
#include <iostream> #include <iostream>
TEST_CASE("Elementary Matrix Operations", "Matrix") TEST_CASE("Elementary Matrix Operations", "Matrix") {
{
std::array<float, 4> arr2{5, 6, 7, 8}; std::array<float, 4> arr2{5, 6, 7, 8};
Matrix<2, 2> mat1{1, 2, 3, 4}; Matrix<2, 2> mat1{1, 2, 3, 4};
Matrix<2, 2> mat2{arr2}; Matrix<2, 2> mat2{arr2};
Matrix<2, 2> mat3{}; Matrix<2, 2> mat3{};
SECTION("Initialization") SECTION("Initialization") {
{
// array initialization // array initialization
REQUIRE(mat1.Get(0, 0) == 1); REQUIRE(mat1.Get(0, 0) == 1);
REQUIRE(mat1.Get(0, 1) == 2); REQUIRE(mat1.Get(0, 1) == 2);
@@ -40,17 +38,14 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
// large matrix // large matrix
Matrix<255, 255> mat6{}; Matrix<255, 255> mat6{};
mat6.Fill(4); mat6.Fill(4);
for (uint8_t row{0}; row < 255; row++) for (uint8_t row{0}; row < 255; row++) {
{ for (uint8_t column{0}; column < 255; column++) {
for (uint8_t column{0}; column < 255; column++)
{
REQUIRE(mat6.Get(row, column) == 4); REQUIRE(mat6.Get(row, column) == 4);
} }
} }
} }
SECTION("Fill") SECTION("Fill") {
{
mat1.Fill(0); mat1.Fill(0);
REQUIRE(mat1.Get(0, 0) == 0); REQUIRE(mat1.Get(0, 0) == 0);
REQUIRE(mat1.Get(0, 1) == 0); REQUIRE(mat1.Get(0, 1) == 0);
@@ -70,12 +65,10 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(mat3.Get(1, 1) == -20); REQUIRE(mat3.Get(1, 1) == -20);
} }
SECTION("Addition") SECTION("Addition") {
{
std::string strBuf1 = ""; std::string strBuf1 = "";
mat1.ToString(strBuf1); mat1.ToString(strBuf1);
std::cout << "Matrix 1:\n" std::cout << "Matrix 1:\n" << strBuf1 << std::endl;
<< strBuf1 << std::endl;
mat1.Add(mat2, mat3); mat1.Add(mat2, mat3);
@@ -93,8 +86,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(mat3.Get(1, 1) == 12); REQUIRE(mat3.Get(1, 1) == 12);
} }
SECTION("Subtraction") SECTION("Subtraction") {
{
mat1.Sub(mat2, mat3); mat1.Sub(mat2, mat3);
REQUIRE(mat3.Get(0, 0) == -4); REQUIRE(mat3.Get(0, 0) == -4);
@@ -111,8 +103,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(mat3.Get(1, 1) == -4); REQUIRE(mat3.Get(1, 1) == -4);
} }
SECTION("Multiplication") SECTION("Multiplication") {
{
mat1.Mult(mat2, mat3); mat1.Mult(mat2, mat3);
REQUIRE(mat3.Get(0, 0) == 19); REQUIRE(mat3.Get(0, 0) == 19);
@@ -131,8 +122,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
// TODO: You need to add non-square multiplications to this. // TODO: You need to add non-square multiplications to this.
} }
SECTION("Scalar Multiplication") SECTION("Scalar Multiplication") {
{
mat1.Mult(2, mat3); mat1.Mult(2, mat3);
REQUIRE(mat3.Get(0, 0) == 2); REQUIRE(mat3.Get(0, 0) == 2);
@@ -141,8 +131,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(mat3.Get(1, 1) == 8); REQUIRE(mat3.Get(1, 1) == 8);
} }
SECTION("Element Multiply") SECTION("Element Multiply") {
{
mat1.ElementMultiply(mat2, mat3); mat1.ElementMultiply(mat2, mat3);
REQUIRE(mat3.Get(0, 0) == 5); REQUIRE(mat3.Get(0, 0) == 5);
@@ -151,8 +140,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(mat3.Get(1, 1) == 32); REQUIRE(mat3.Get(1, 1) == 32);
} }
SECTION("Element Divide") SECTION("Element Divide") {
{
mat1.ElementDivide(mat2, mat3); mat1.ElementDivide(mat2, mat3);
REQUIRE_THAT(mat3.Get(0, 0), Catch::Matchers::WithinRel(0.2f, 1e-6f)); REQUIRE_THAT(mat3.Get(0, 0), Catch::Matchers::WithinRel(0.2f, 1e-6f));
@@ -161,8 +149,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE_THAT(mat3.Get(1, 1), Catch::Matchers::WithinRel(0.5f, 1e-6f)); REQUIRE_THAT(mat3.Get(1, 1), Catch::Matchers::WithinRel(0.5f, 1e-6f));
} }
SECTION("Minor Matrix") SECTION("Minor Matrix") {
{
// what about matrices of 0,0 or 1,1? // what about matrices of 0,0 or 1,1?
// minor matrix for 2x2 matrix // minor matrix for 2x2 matrix
Matrix<1, 1> minorMat1{}; Matrix<1, 1> minorMat1{};
@@ -198,8 +185,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(minorMat4.Get(1, 1) == 5); REQUIRE(minorMat4.Get(1, 1) == 5);
} }
SECTION("Determinant") SECTION("Determinant") {
{
float det1 = mat1.Det(); float det1 = mat1.Det();
REQUIRE_THAT(det1, Catch::Matchers::WithinRel(-2.0F, 1e-6f)); REQUIRE_THAT(det1, Catch::Matchers::WithinRel(-2.0F, 1e-6f));
@@ -216,8 +202,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE_THAT(det5, Catch::Matchers::WithinRel(6.0F, 1e-6f)); REQUIRE_THAT(det5, Catch::Matchers::WithinRel(6.0F, 1e-6f));
} }
SECTION("Matrix of Minors") SECTION("Matrix of Minors") {
{
mat1.MatrixOfMinors(mat3); mat1.MatrixOfMinors(mat3);
REQUIRE_THAT(mat3.Get(0, 0), Catch::Matchers::WithinRel(4.0F, 1e-6f)); REQUIRE_THAT(mat3.Get(0, 0), Catch::Matchers::WithinRel(4.0F, 1e-6f));
REQUIRE_THAT(mat3.Get(0, 1), Catch::Matchers::WithinRel(3.0F, 1e-6f)); REQUIRE_THAT(mat3.Get(0, 1), Catch::Matchers::WithinRel(3.0F, 1e-6f));
@@ -238,8 +223,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE_THAT(mat5.Get(2, 2), Catch::Matchers::WithinRel(-3.0F, 1e-6f)); REQUIRE_THAT(mat5.Get(2, 2), Catch::Matchers::WithinRel(-3.0F, 1e-6f));
} }
SECTION("Invert") SECTION("Invert") {
{
mat3 = mat1.Invert(); mat3 = mat1.Invert();
REQUIRE_THAT(mat3.Get(0, 0), Catch::Matchers::WithinRel(-2.0F, 1e-6f)); REQUIRE_THAT(mat3.Get(0, 0), Catch::Matchers::WithinRel(-2.0F, 1e-6f));
REQUIRE_THAT(mat3.Get(0, 1), Catch::Matchers::WithinRel(1.0F, 1e-6f)); REQUIRE_THAT(mat3.Get(0, 1), Catch::Matchers::WithinRel(1.0F, 1e-6f));
@@ -247,8 +231,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE_THAT(mat3.Get(1, 1), Catch::Matchers::WithinRel(-0.5F, 1e-6f)); REQUIRE_THAT(mat3.Get(1, 1), Catch::Matchers::WithinRel(-0.5F, 1e-6f));
}; };
SECTION("Transpose") SECTION("Transpose") {
{
// transpose a square matrix // transpose a square matrix
mat3 = mat1.Transpose(); mat3 = mat1.Transpose();
@@ -271,8 +254,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(mat5.Get(2, 1) == 6); REQUIRE(mat5.Get(2, 1) == 6);
} }
SECTION("Normalize") SECTION("Normalize") {
{
mat1.Normalize(mat3); mat1.Normalize(mat3);
float sqrt_30{sqrt(30)}; float sqrt_30{sqrt(30)};
@@ -292,8 +274,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
Catch::Matchers::WithinRel(0.957591346325f, 1e-6f)); Catch::Matchers::WithinRel(0.957591346325f, 1e-6f));
} }
SECTION("GET ROW") SECTION("GET ROW") {
{
Matrix<1, 2> mat1Rows{}; Matrix<1, 2> mat1Rows{};
mat1.GetRow(0, mat1Rows); mat1.GetRow(0, mat1Rows);
REQUIRE(mat1Rows.Get(0, 0) == 1); REQUIRE(mat1Rows.Get(0, 0) == 1);
@@ -304,8 +285,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(mat1Rows.Get(0, 1) == 4); REQUIRE(mat1Rows.Get(0, 1) == 4);
} }
SECTION("GET COLUMN") SECTION("GET COLUMN") {
{
Matrix<2, 1> mat1Columns{}; Matrix<2, 1> mat1Columns{};
mat1.GetColumn(0, mat1Columns); mat1.GetColumn(0, mat1Columns);
REQUIRE(mat1Columns.Get(0, 0) == 1); REQUIRE(mat1Columns.Get(0, 0) == 1);
@@ -316,12 +296,8 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(mat1Columns.Get(1, 0) == 4); REQUIRE(mat1Columns.Get(1, 0) == 4);
} }
SECTION("Get Sub-Matrices") SECTION("Get Sub-Matrices") {
{ Matrix<3, 3> mat4{1, 2, 3, 4, 5, 6, 7, 8, 9};
Matrix<3, 3> mat4{
1, 2, 3,
4, 5, 6,
7, 8, 9};
Matrix<2, 2> mat5 = mat4.SubMatrix<2, 2, 0, 0>(); Matrix<2, 2> mat5 = mat4.SubMatrix<2, 2, 0, 0>();
@@ -347,12 +323,8 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(mat7.Get(0, 2) == 3); REQUIRE(mat7.Get(0, 2) == 3);
} }
SECTION("Set Sub-Matrices") SECTION("Set Sub-Matrices") {
{ Matrix<3, 3> startMatrix{1, 2, 3, 4, 5, 6, 7, 8, 9};
Matrix<3, 3> startMatrix{
1, 2, 3,
4, 5, 6,
7, 8, 9};
Matrix<3, 3> mat4 = startMatrix; Matrix<3, 3> mat4 = startMatrix;
Matrix<2, 2> mat5{10, 11, 12, 13}; Matrix<2, 2> mat5{10, 11, 12, 13};
@@ -381,144 +353,4 @@ TEST_CASE("Elementary Matrix Operations", "Matrix")
REQUIRE(mat4.Get(0, 1) == 11); REQUIRE(mat4.Get(0, 1) == 11);
REQUIRE(mat4.Get(0, 2) == 12); REQUIRE(mat4.Get(0, 2) == 12);
} }
}
// basically re-run all of the previous tests with huge matrices and time the
// results.
TEST_CASE("Timing Tests", "Matrix")
{
std::array<float, 50 * 50> arr1{};
for (uint16_t i{0}; i < 50 * 50; i++)
{
arr1[i] = i;
}
std::array<float, 50 * 50> arr2{5, 6, 7, 8};
for (uint16_t i{50 * 50}; i < 2 * 50 * 50; i++)
{
arr2[i] = i;
}
Matrix<50, 50> mat1{arr1};
Matrix<50, 50> mat2{arr2};
Matrix<50, 50> mat3{};
// A smaller matrix to use for really badly optimized operations
Matrix<4, 4> mat4{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
Matrix<4, 4> mat5{};
SECTION("Addition")
{
for (uint32_t i{0}; i < 10000; i++)
{
mat3 = mat1 + mat2;
}
}
SECTION("Subtraction")
{
for (uint32_t i{0}; i < 10000; i++)
{
mat3 = mat1 - mat2;
}
}
SECTION("Multiplication")
{
for (uint32_t i{0}; i < 1000; i++)
{
mat3 = mat1 * mat2;
}
}
SECTION("Scalar Multiplication")
{
for (uint32_t i{0}; i < 10000; i++)
{
mat3 = mat1 * 3;
}
}
SECTION("Element Multiply")
{
for (uint32_t i{0}; i < 10000; i++)
{
mat1.ElementMultiply(mat2, mat3);
}
}
SECTION("Element Divide")
{
for (uint32_t i{0}; i < 10000; i++)
{
mat1.ElementDivide(mat2, mat3);
}
}
SECTION("Minor Matrix")
{
// what about matrices of 0,0 or 1,1?
// minor matrix for 2x2 matrix
Matrix<49, 49> minorMat1{};
for (uint32_t i{0}; i < 10000; i++)
{
mat1.MinorMatrix(minorMat1, 0, 0);
}
}
SECTION("Determinant")
{
for (uint32_t i{0}; i < 100000; i++)
{
float det1 = mat4.Det();
}
}
SECTION("Matrix of Minors")
{
for (uint32_t i{0}; i < 100000; i++)
{
mat4.MatrixOfMinors(mat5);
}
}
SECTION("Invert")
{
for (uint32_t i{0}; i < 100000; i++)
{
mat5 = mat4.Invert();
}
};
SECTION("Transpose")
{
for (uint32_t i{0}; i < 10000; i++)
{
mat3 = mat1.Transpose();
}
}
SECTION("Normalize")
{
for (uint32_t i{0}; i < 10000; i++)
{
mat1.Normalize(mat3);
}
}
SECTION("GET ROW")
{
Matrix<1, 50> mat1Rows{};
for (uint32_t i{0}; i < 1000000; i++)
{
mat1.GetRow(0, mat1Rows);
}
}
SECTION("GET COLUMN")
{
Matrix<50, 1> mat1Columns{};
for (uint32_t i{0}; i < 1000000; i++)
{
mat1.GetColumn(0, mat1Columns);
}
}
} }

View File

@@ -0,0 +1,119 @@
// include the unit test framework first
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
// include the module you're going to test next
#include "Matrix.hpp"
// any other libraries
#include <array>
#include <cmath>
// basically re-run all of the matrix tests with huge matrices and time the
// results.
TEST_CASE("Timing Tests", "Matrix") {
std::array<float, 50 * 50> arr1{};
for (uint16_t i{0}; i < 50 * 50; i++) {
arr1[i] = i;
}
std::array<float, 50 * 50> arr2{5, 6, 7, 8};
for (uint16_t i{50 * 50}; i < 2 * 50 * 50; i++) {
arr2[i] = i;
}
Matrix<50, 50> mat1{arr1};
Matrix<50, 50> mat2{arr2};
Matrix<50, 50> mat3{};
// A smaller matrix to use for really badly optimized operations
Matrix<4, 4> mat4{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
Matrix<4, 4> mat5{};
SECTION("Addition") {
for (uint32_t i{0}; i < 10000; i++) {
mat3 = mat1 + mat2;
}
}
SECTION("Subtraction") {
for (uint32_t i{0}; i < 10000; i++) {
mat3 = mat1 - mat2;
}
}
SECTION("Multiplication") {
for (uint32_t i{0}; i < 1000; i++) {
mat3 = mat1 * mat2;
}
}
SECTION("Scalar Multiplication") {
for (uint32_t i{0}; i < 10000; i++) {
mat3 = mat1 * 3;
}
}
SECTION("Element Multiply") {
for (uint32_t i{0}; i < 10000; i++) {
mat1.ElementMultiply(mat2, mat3);
}
}
SECTION("Element Divide") {
for (uint32_t i{0}; i < 10000; i++) {
mat1.ElementDivide(mat2, mat3);
}
}
SECTION("Minor Matrix") {
// what about matrices of 0,0 or 1,1?
// minor matrix for 2x2 matrix
Matrix<49, 49> minorMat1{};
for (uint32_t i{0}; i < 10000; i++) {
mat1.MinorMatrix(minorMat1, 0, 0);
}
}
SECTION("Determinant") {
for (uint32_t i{0}; i < 100000; i++) {
float det1 = mat4.Det();
}
}
SECTION("Matrix of Minors") {
for (uint32_t i{0}; i < 100000; i++) {
mat4.MatrixOfMinors(mat5);
}
}
SECTION("Invert") {
for (uint32_t i{0}; i < 100000; i++) {
mat5 = mat4.Invert();
}
};
SECTION("Transpose") {
for (uint32_t i{0}; i < 10000; i++) {
mat3 = mat1.Transpose();
}
}
SECTION("Normalize") {
for (uint32_t i{0}; i < 10000; i++) {
mat1.Normalize(mat3);
}
}
SECTION("GET ROW") {
Matrix<1, 50> mat1Rows{};
for (uint32_t i{0}; i < 1000000; i++) {
mat1.GetRow(0, mat1Rows);
}
}
SECTION("GET COLUMN") {
Matrix<50, 1> mat1Columns{};
for (uint32_t i{0}; i < 1000000; i++) {
mat1.GetColumn(0, mat1Columns);
}
}
}

View File

@@ -1,7 +0,0 @@
# be in the root folder of this project when you run this
cd build/
ninja matrix-tests
echo "Running tests. This will take a while."
./unit-tests/matrix-tests -n "Timing Tests" -d yes > ../unit-tests/matrix-test-timings-temp.txt
cd ../unit-tests/
python3 test-timing-post-process.py