From 54d9699df80576eed9cbf0d8f72ecbf183fa302a Mon Sep 17 00:00:00 2001 From: Cynopolis Date: Wed, 21 May 2025 17:48:18 -0400 Subject: [PATCH 1/3] Added a merge checker script that has to run before you can merge to main Updated merge checker and seperated the matrix tests fro mthe timing tests Added matrix test timings Timings get auto-comitted Update matrix-timing-tests timings [skip ci] Updated readme Update matrix-timing-tests timings [skip ci] Fixing auto-checkout issues updated readme Update matrix-timing-tests timings [skip ci] Split timing tests into its own job Update matrix-timing-tests timings [skip ci] --- .gitea/workflows/Matrix-Timing.yaml | 72 ++++++ .gitea/workflows/Merge-Checker.yaml | 26 ++- README.md | 4 + unit-tests/CMakeLists.txt | 9 + unit-tests/matrix-test-timings.txt | 14 -- unit-tests/matrix-tests.cpp | 216 ++---------------- unit-tests/matrix-timing-tests.cpp | 119 ++++++++++ unit-tests/run-matrix-timing-test.sh | 7 - .../timing-results/matrix-timing-tests.txt | 56 +++++ 9 files changed, 304 insertions(+), 219 deletions(-) create mode 100644 .gitea/workflows/Matrix-Timing.yaml delete mode 100644 unit-tests/matrix-test-timings.txt create mode 100644 unit-tests/matrix-timing-tests.cpp delete mode 100755 unit-tests/run-matrix-timing-test.sh create mode 100644 unit-tests/timing-results/matrix-timing-tests.txt diff --git a/.gitea/workflows/Matrix-Timing.yaml b/.gitea/workflows/Matrix-Timing.yaml new file mode 100644 index 0000000..8e9ff40 --- /dev/null +++ b/.gitea/workflows/Matrix-Timing.yaml @@ -0,0 +1,72 @@ +name: Merge-Checker + +on: + pull_request: + branches: ["**"] + paths-ignore: + - 'unit-tests/timing-results/**' + +jobs: + build_and_test: + runs-on: ubuntu-latest + + steps: + - name: Checkout source code + uses: actions/checkout@v3 + with: + persist-credentials: true + fetch-depth: 0 + + - name: Install dependencies (CMake + Ninja + build tools) + run: | + sudo apt-get update + sudo apt-get install -y cmake ninja-build build-essential time git + + - name: Configure project with CMake + run: cmake -G Ninja -S . -B build/ + + - name: Build with Ninja + run: ninja -C build/ + + - 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 + 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 stash + echo "Checking out source branch $BRANCH_NAME" + git fetch origin "$BRANCH_NAME" + git checkout "$BRANCH_NAME" + git pull + + echo "Checking if last commit was a timing update" + LAST_COMMIT_MSG=$(git log -1 --pretty=%B) + + if echo "$LAST_COMMIT_MSG" | grep -q "Update matrix-timing-tests timings"; then + echo "Last commit was a timing update, skipping commit." + exit 0 + else + echo "Last commit name was: $LAST_COMMIT_MSG" + git stash pop + fi + + 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 "$BRANCH_NAME" + fi diff --git a/.gitea/workflows/Merge-Checker.yaml b/.gitea/workflows/Merge-Checker.yaml index 4a220dd..4466954 100644 --- a/.gitea/workflows/Merge-Checker.yaml +++ b/.gitea/workflows/Merge-Checker.yaml @@ -3,24 +3,38 @@ name: Merge-Checker on: pull_request: branches: ["**"] + paths-ignore: + - 'unit-tests/timing-results/**' jobs: - build: + build_and_test: runs-on: ubuntu-latest steps: - name: Checkout source code 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: | 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 - run: | - cmake -G Ninja -S . -B build/ + run: cmake -G Ninja -S . -B build/ - name: Build with Ninja + run: ninja -C build/ + + - name: Run all unit tests except matrix-timing-tests 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 \ No newline at end of file diff --git a/README.md b/README.md index b62bce7..6bb9722 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ +# Introduction This matrix math library is focused on embedded development and avoids any heap memory allocation unless you explicitly ask for it. +It uses templates to pre-allocate matrices on the stack. + +There are still several operations that are works in progress \ No newline at end of file diff --git a/unit-tests/CMakeLists.txt b/unit-tests/CMakeLists.txt index 9ee3fa1..a57babd 100644 --- a/unit-tests/CMakeLists.txt +++ b/unit-tests/CMakeLists.txt @@ -16,6 +16,15 @@ target_link_libraries(matrix-tests 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 add_executable(vector-3d-tests vector-tests.cpp) diff --git a/unit-tests/matrix-test-timings.txt b/unit-tests/matrix-test-timings.txt deleted file mode 100644 index 1e2dff2..0000000 --- a/unit-tests/matrix-test-timings.txt +++ /dev/null @@ -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 diff --git a/unit-tests/matrix-tests.cpp b/unit-tests/matrix-tests.cpp index a6818a7..e6d8862 100644 --- a/unit-tests/matrix-tests.cpp +++ b/unit-tests/matrix-tests.cpp @@ -10,15 +10,13 @@ #include #include -TEST_CASE("Elementary Matrix Operations", "Matrix") -{ +TEST_CASE("Elementary Matrix Operations", "Matrix") { std::array arr2{5, 6, 7, 8}; Matrix<2, 2> mat1{1, 2, 3, 4}; Matrix<2, 2> mat2{arr2}; Matrix<2, 2> mat3{}; - SECTION("Initialization") - { + SECTION("Initialization") { // array initialization REQUIRE(mat1.Get(0, 0) == 1); REQUIRE(mat1.Get(0, 1) == 2); @@ -40,17 +38,14 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") // large matrix Matrix<255, 255> mat6{}; mat6.Fill(4); - for (uint8_t row{0}; row < 255; row++) - { - for (uint8_t column{0}; column < 255; column++) - { + for (uint8_t row{0}; row < 255; row++) { + for (uint8_t column{0}; column < 255; column++) { REQUIRE(mat6.Get(row, column) == 4); } } } - SECTION("Fill") - { + SECTION("Fill") { mat1.Fill(0); REQUIRE(mat1.Get(0, 0) == 0); REQUIRE(mat1.Get(0, 1) == 0); @@ -70,12 +65,10 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat3.Get(1, 1) == -20); } - SECTION("Addition") - { + SECTION("Addition") { std::string strBuf1 = ""; mat1.ToString(strBuf1); - std::cout << "Matrix 1:\n" - << strBuf1 << std::endl; + std::cout << "Matrix 1:\n" << strBuf1 << std::endl; mat1.Add(mat2, mat3); @@ -93,8 +86,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat3.Get(1, 1) == 12); } - SECTION("Subtraction") - { + SECTION("Subtraction") { mat1.Sub(mat2, mat3); REQUIRE(mat3.Get(0, 0) == -4); @@ -111,8 +103,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat3.Get(1, 1) == -4); } - SECTION("Multiplication") - { + SECTION("Multiplication") { mat1.Mult(mat2, mat3); 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. } - SECTION("Scalar Multiplication") - { + SECTION("Scalar Multiplication") { mat1.Mult(2, mat3); REQUIRE(mat3.Get(0, 0) == 2); @@ -141,8 +131,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat3.Get(1, 1) == 8); } - SECTION("Element Multiply") - { + SECTION("Element Multiply") { mat1.ElementMultiply(mat2, mat3); REQUIRE(mat3.Get(0, 0) == 5); @@ -151,8 +140,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat3.Get(1, 1) == 32); } - SECTION("Element Divide") - { + SECTION("Element Divide") { mat1.ElementDivide(mat2, mat3); 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)); } - SECTION("Minor Matrix") - { + SECTION("Minor Matrix") { // what about matrices of 0,0 or 1,1? // minor matrix for 2x2 matrix Matrix<1, 1> minorMat1{}; @@ -198,8 +185,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(minorMat4.Get(1, 1) == 5); } - SECTION("Determinant") - { + SECTION("Determinant") { float det1 = mat1.Det(); 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)); } - SECTION("Matrix of Minors") - { + SECTION("Matrix of Minors") { mat1.MatrixOfMinors(mat3); 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)); @@ -238,8 +223,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE_THAT(mat5.Get(2, 2), Catch::Matchers::WithinRel(-3.0F, 1e-6f)); } - SECTION("Invert") - { + SECTION("Invert") { mat3 = mat1.Invert(); 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)); @@ -247,8 +231,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE_THAT(mat3.Get(1, 1), Catch::Matchers::WithinRel(-0.5F, 1e-6f)); }; - SECTION("Transpose") - { + SECTION("Transpose") { // transpose a square matrix mat3 = mat1.Transpose(); @@ -271,8 +254,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat5.Get(2, 1) == 6); } - SECTION("Normalize") - { + SECTION("Normalize") { mat1.Normalize(mat3); float sqrt_30{sqrt(30)}; @@ -292,8 +274,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") Catch::Matchers::WithinRel(0.957591346325f, 1e-6f)); } - SECTION("GET ROW") - { + SECTION("GET ROW") { Matrix<1, 2> mat1Rows{}; mat1.GetRow(0, mat1Rows); REQUIRE(mat1Rows.Get(0, 0) == 1); @@ -304,8 +285,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat1Rows.Get(0, 1) == 4); } - SECTION("GET COLUMN") - { + SECTION("GET COLUMN") { Matrix<2, 1> mat1Columns{}; mat1.GetColumn(0, mat1Columns); REQUIRE(mat1Columns.Get(0, 0) == 1); @@ -316,12 +296,8 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat1Columns.Get(1, 0) == 4); } - SECTION("Get Sub-Matrices") - { - Matrix<3, 3> mat4{ - 1, 2, 3, - 4, 5, 6, - 7, 8, 9}; + SECTION("Get Sub-Matrices") { + Matrix<3, 3> mat4{1, 2, 3, 4, 5, 6, 7, 8, 9}; 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); } - SECTION("Set Sub-Matrices") - { - Matrix<3, 3> startMatrix{ - 1, 2, 3, - 4, 5, 6, - 7, 8, 9}; + SECTION("Set Sub-Matrices") { + Matrix<3, 3> startMatrix{1, 2, 3, 4, 5, 6, 7, 8, 9}; Matrix<3, 3> mat4 = startMatrix; 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, 2) == 12); } -} - -// basically re-run all of the previous tests with huge matrices and time the -// results. -TEST_CASE("Timing Tests", "Matrix") -{ - std::array arr1{}; - for (uint16_t i{0}; i < 50 * 50; i++) - { - arr1[i] = i; - } - std::array 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); - } - } } \ No newline at end of file diff --git a/unit-tests/matrix-timing-tests.cpp b/unit-tests/matrix-timing-tests.cpp new file mode 100644 index 0000000..9c63afe --- /dev/null +++ b/unit-tests/matrix-timing-tests.cpp @@ -0,0 +1,119 @@ +// include the unit test framework first +#include +#include + +// include the module you're going to test next +#include "Matrix.hpp" + +// any other libraries +#include +#include + +// basically re-run all of the matrix tests with huge matrices and time the +// results. +TEST_CASE("Timing Tests", "Matrix") { + std::array arr1{}; + for (uint16_t i{0}; i < 50 * 50; i++) { + arr1[i] = i; + } + std::array 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); + } + } +} \ No newline at end of file diff --git a/unit-tests/run-matrix-timing-test.sh b/unit-tests/run-matrix-timing-test.sh deleted file mode 100755 index f6d6638..0000000 --- a/unit-tests/run-matrix-timing-test.sh +++ /dev/null @@ -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 \ No newline at end of file diff --git a/unit-tests/timing-results/matrix-timing-tests.txt b/unit-tests/timing-results/matrix-timing-tests.txt new file mode 100644 index 0000000..953c985 --- /dev/null +++ b/unit-tests/timing-results/matrix-timing-tests.txt @@ -0,0 +1,56 @@ +Randomness seeded to: 2680752211 +0.179 s: Addition +0.179 s: Timing Tests +0.172 s: Subtraction +0.172 s: Timing Tests +1.894 s: Multiplication +1.894 s: Timing Tests +0.128 s: Scalar Multiplication +0.128 s: Timing Tests +0.177 s: Element Multiply +0.177 s: Timing Tests +0.176 s: Element Divide +0.176 s: Timing Tests +0.150 s: Minor Matrix +0.150 s: Timing Tests +0.102 s: Determinant +0.102 s: Timing Tests +0.416 s: Matrix of Minors +0.416 s: Timing Tests +0.110 s: Invert +0.110 s: Timing Tests +0.124 s: Transpose +0.124 s: Timing Tests +0.189 s: Normalize +0.189 s: Timing Tests +0.006 s: GET ROW +0.006 s: Timing Tests +0.234 s: GET COLUMN +0.234 s: Timing Tests +=============================================================================== +test cases: 1 | 1 passed +assertions: - none - + + Command being timed: "build/unit-tests/matrix-timing-tests -d yes" + User time (seconds): 4.05 + System time (seconds): 0.00 + Percent of CPU this job got: 99% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:04.06 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 3200 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 186 + Minor (reclaiming a frame) page faults: 172 + Voluntary context switches: 1 + Involuntary context switches: 40 + Swaps: 0 + File system inputs: 12 + File system outputs: 1 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 -- 2.49.1 From 32c2a5cef2a663f8d62d49c5246cf3dbf3da2f43 Mon Sep 17 00:00:00 2001 From: Cynopolis Date: Thu, 29 May 2025 11:19:25 -0400 Subject: [PATCH 2/3] Added a check to see if the timing results have signifigantly changed Update matrix-timing-tests timings [skip ci] Fixing timing test runner Update matrix-timing-tests timings Removed the seperate benchmark action --- .gitea/workflows/Matrix-Timing.yaml | 72 ------------------- .gitea/workflows/Merge-Checker.yaml | 68 +++++++++++++++++- .../timing-results/matrix-timing-tests.txt | 62 ++++++++-------- 3 files changed, 96 insertions(+), 106 deletions(-) delete mode 100644 .gitea/workflows/Matrix-Timing.yaml diff --git a/.gitea/workflows/Matrix-Timing.yaml b/.gitea/workflows/Matrix-Timing.yaml deleted file mode 100644 index 8e9ff40..0000000 --- a/.gitea/workflows/Matrix-Timing.yaml +++ /dev/null @@ -1,72 +0,0 @@ -name: Merge-Checker - -on: - pull_request: - branches: ["**"] - paths-ignore: - - 'unit-tests/timing-results/**' - -jobs: - build_and_test: - runs-on: ubuntu-latest - - steps: - - name: Checkout source code - uses: actions/checkout@v3 - with: - persist-credentials: true - fetch-depth: 0 - - - name: Install dependencies (CMake + Ninja + build tools) - run: | - sudo apt-get update - sudo apt-get install -y cmake ninja-build build-essential time git - - - name: Configure project with CMake - run: cmake -G Ninja -S . -B build/ - - - name: Build with Ninja - run: ninja -C build/ - - - 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 - 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 stash - echo "Checking out source branch $BRANCH_NAME" - git fetch origin "$BRANCH_NAME" - git checkout "$BRANCH_NAME" - git pull - - echo "Checking if last commit was a timing update" - LAST_COMMIT_MSG=$(git log -1 --pretty=%B) - - if echo "$LAST_COMMIT_MSG" | grep -q "Update matrix-timing-tests timings"; then - echo "Last commit was a timing update, skipping commit." - exit 0 - else - echo "Last commit name was: $LAST_COMMIT_MSG" - git stash pop - fi - - 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 "$BRANCH_NAME" - fi diff --git a/.gitea/workflows/Merge-Checker.yaml b/.gitea/workflows/Merge-Checker.yaml index 4466954..886a17f 100644 --- a/.gitea/workflows/Merge-Checker.yaml +++ b/.gitea/workflows/Merge-Checker.yaml @@ -3,8 +3,6 @@ name: Merge-Checker on: pull_request: branches: ["**"] - paths-ignore: - - 'unit-tests/timing-results/**' jobs: build_and_test: @@ -37,4 +35,68 @@ jobs: else echo "Warning: $test_exec not found or not executable" fi - done \ No newline at end of file + done + - name: Run matrix-timing-tests + 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 + cat unit-tests/timing-results/matrix-timing-tests.txt + else + echo "matrix-timing-tests executable not found or not executable" + exit 1 + fi + + - name: Compare timing results + id: check_diff + run: | + git show origin/${{ github.event.pull_request.head.ref }}:unit-tests/timing-results/matrix-timing-tests.txt > old.txt || echo "" > old.txt + cp unit-tests/timing-results/matrix-timing-tests.txt new.txt + + echo "Comparing timing results for changes ≥ 0.1s (ignoring 'Timing Tests' lines)..." + + changed=0 + + awk -v changed_ref=/tmp/timings_changed.flag ' + BEGIN { + change_threshold = 0.1 + } + FILENAME == "old.txt" && /^[0-9]+\.[0-9]+ s: / { + label = substr($0, index($0, ":") + 2) + if (label != "Timing Tests") { + label_times[label] = $1 + } + } + FILENAME == "new.txt" && /^[0-9]+\.[0-9]+ s: / { + new_time = $1 + label = substr($0, index($0, ":") + 2) + if (label == "Timing Tests") next + + old_time = label_times[label] + delta = new_time - old_time + if (delta < 0) delta = -delta + + if (old_time != "" && delta >= change_threshold) { + printf "⚠️ %.3f s → %.3f s: %s (Δ=%.3f s)\n", old_time, new_time, label, delta + system("touch " changed_ref) + } else if (old_time == "") { + printf "🆕 New timing entry: %.3f s: %s\n", new_time, label + system("touch " changed_ref) + } + } + END { + if (!system("test -f " changed_ref)) { + exit 0 + } else { + print "✅ Timings haven’t changed significantly (Δ < 0.1s)." + exit 0 + } + } + ' old.txt new.txt + + if [ -f /tmp/timings_changed.flag ]; then + echo "timings_changed=true" >> $GITHUB_OUTPUT + else + echo "timings_changed=false" >> $GITHUB_OUTPUT + fi \ No newline at end of file diff --git a/unit-tests/timing-results/matrix-timing-tests.txt b/unit-tests/timing-results/matrix-timing-tests.txt index 953c985..1e728e8 100644 --- a/unit-tests/timing-results/matrix-timing-tests.txt +++ b/unit-tests/timing-results/matrix-timing-tests.txt @@ -1,32 +1,32 @@ -Randomness seeded to: 2680752211 -0.179 s: Addition -0.179 s: Timing Tests -0.172 s: Subtraction -0.172 s: Timing Tests -1.894 s: Multiplication -1.894 s: Timing Tests -0.128 s: Scalar Multiplication -0.128 s: Timing Tests -0.177 s: Element Multiply +Randomness seeded to: 2444679151 +0.180 s: Addition +0.180 s: Timing Tests +0.177 s: Subtraction 0.177 s: Timing Tests -0.176 s: Element Divide -0.176 s: Timing Tests -0.150 s: Minor Matrix -0.150 s: Timing Tests -0.102 s: Determinant -0.102 s: Timing Tests -0.416 s: Matrix of Minors -0.416 s: Timing Tests -0.110 s: Invert -0.110 s: Timing Tests -0.124 s: Transpose -0.124 s: Timing Tests -0.189 s: Normalize -0.189 s: Timing Tests +1.868 s: Multiplication +1.868 s: Timing Tests +0.127 s: Scalar Multiplication +0.127 s: Timing Tests +0.173 s: Element Multiply +0.173 s: Timing Tests +0.178 s: Element Divide +0.178 s: Timing Tests +0.172 s: Minor Matrix +0.172 s: Timing Tests +0.103 s: Determinant +0.103 s: Timing Tests +0.411 s: Matrix of Minors +0.411 s: Timing Tests +0.109 s: Invert +0.109 s: Timing Tests +0.122 s: Transpose +0.122 s: Timing Tests +0.190 s: Normalize +0.190 s: Timing Tests 0.006 s: GET ROW 0.006 s: Timing Tests -0.234 s: GET COLUMN -0.234 s: Timing Tests +0.235 s: GET COLUMN +0.235 s: Timing Tests =============================================================================== test cases: 1 | 1 passed assertions: - none - @@ -34,18 +34,18 @@ assertions: - none - Command being timed: "build/unit-tests/matrix-timing-tests -d yes" User time (seconds): 4.05 System time (seconds): 0.00 - Percent of CPU this job got: 99% - Elapsed (wall clock) time (h:mm:ss or m:ss): 0:04.06 + Percent of CPU this job got: 100% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:04.05 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 3200 Average resident set size (kbytes): 0 - Major (requiring I/O) page faults: 186 - Minor (reclaiming a frame) page faults: 172 + Major (requiring I/O) page faults: 184 + Minor (reclaiming a frame) page faults: 171 Voluntary context switches: 1 - Involuntary context switches: 40 + Involuntary context switches: 26 Swaps: 0 File system inputs: 12 File system outputs: 1 -- 2.49.1 From 296f233b28c9309c1ae6a08fcbc47fb59ebed5b3 Mon Sep 17 00:00:00 2001 From: Cynopolis Date: Thu, 29 May 2025 16:35:52 -0400 Subject: [PATCH 3/3] Updated README --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6bb9722..2fcb01c 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,8 @@ This matrix math library is focused on embedded development and avoids any heap memory allocation unless you explicitly ask for it. It uses templates to pre-allocate matrices on the stack. -There are still several operations that are works in progress \ No newline at end of file +There are still several operations that are works in progress such as: +TODO: Add a function to calculate eigenvalues/vectors +TODO: Add a function to compute RREF +TODO: Add a function for SVD decomposition +TODO: Add a function for LQ decomposition \ No newline at end of file -- 2.49.1