From fb16c6f61b26285c5bf24519deae159d27976c01 Mon Sep 17 00:00:00 2001 From: Quinn Henthorne Date: Fri, 13 Dec 2024 15:50:53 -0500 Subject: [PATCH] Added timing tests for matrix math --- unit-tests/.gitignore | 1 + unit-tests/matrix-test-timings.txt | 14 ++++ unit-tests/matrix-tests.cpp | 98 +++++++++++++++++++++- unit-tests/run-matrix-timing-test.sh | 7 ++ unit-tests/test-timing-post-process.py | 108 +++++++++++++++++++++++++ 5 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 unit-tests/.gitignore create mode 100644 unit-tests/matrix-test-timings.txt create mode 100755 unit-tests/run-matrix-timing-test.sh create mode 100644 unit-tests/test-timing-post-process.py diff --git a/unit-tests/.gitignore b/unit-tests/.gitignore new file mode 100644 index 0000000..746a662 --- /dev/null +++ b/unit-tests/.gitignore @@ -0,0 +1 @@ +matrix-test-timings-temp.txt \ No newline at end of file diff --git a/unit-tests/matrix-test-timings.txt b/unit-tests/matrix-test-timings.txt new file mode 100644 index 0000000..57b54b0 --- /dev/null +++ b/unit-tests/matrix-test-timings.txt @@ -0,0 +1,14 @@ +Addition: 0.55 s +Subtraction: 0.548 s +Multiplication: 3.404 s +Scalar Multiplication: 0.453 s +Element Multiply: 0.347 s +Element Divide: 0.347 s +Minor Matrix: 0.44 s +Determinant: 0.251 s +Matrix of Minors: 1.044 s +Invert: 0.262 s +Transpose: 0.26 s +Normalize: 0.333 s +GET ROW: 0.683 s +GET COLUMN: 0.427 s diff --git a/unit-tests/matrix-tests.cpp b/unit-tests/matrix-tests.cpp index 3192715..f2ea173 100644 --- a/unit-tests/matrix-tests.cpp +++ b/unit-tests/matrix-tests.cpp @@ -303,8 +303,98 @@ TEST_CASE("Timing Tests", "Matrix") { Matrix<50, 50> mat2{arr2}; Matrix<50, 50> mat3{}; - // TODO: the timing results should be saved to a temporary text file - // TODO: after the timing results are complete we should read in the old - // result file, calculate the timing increase/decrease and update the file - // with the new results and increase/decrease + // A smaller matrix to use for really badly optimized operations + std::array arr4{1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + Matrix<4, 4> mat4{arr4}; + 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++) { + mat4.Invert(mat5); + } + }; + + SECTION("Transpose") { + for (uint32_t i{0}; i < 10000; i++) { + mat1.Transpose(mat3); + } + } + + 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 new file mode 100755 index 0000000..f6d6638 --- /dev/null +++ b/unit-tests/run-matrix-timing-test.sh @@ -0,0 +1,7 @@ +# 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/test-timing-post-process.py b/unit-tests/test-timing-post-process.py new file mode 100644 index 0000000..ba45a54 --- /dev/null +++ b/unit-tests/test-timing-post-process.py @@ -0,0 +1,108 @@ +class Timing: + def __init__(self, time: float, test_name: str): + self.time = time + self.test_name: str = test_name + self.difference = -1 + + def __eq__(self, other: "Timing"): + return self.test_name.lower() == other.test_name.lower() + + def __sub__(self, other: "Timing"): + return self.time - other.time + + def to_string(self): + return f"{self.test_name}: {self.time} s" + + def to_string_w_diff(self): + diff = self.difference + if diff == -1: + diff = 0 + return f"{self.test_name}: {round(self.time,3)} s, Difference: {round(diff,3)}" + +def create_timing_from_test_line(line: str) -> Timing: + time_end_idx: int = line.find(" ") + if time_end_idx == -1: + return None + try: + time: float = float(line[0:time_end_idx]) + except: + print("Couldn't convert: " + line[0:time_end_idx] + " to a float") + return None + + test_name = line[time_end_idx+4:-1] + return Timing(time, test_name) + +def parse_test_file(file_path: str) -> list[Timing]: + timings: list[Timing] = [] + with open(file_path, 'r') as file: + previous_line = "" + for line in file: + if line.find("Timing Tests") != -1: + timing = create_timing_from_test_line(previous_line) + if timing is not None: + timings.append(timing) + previous_line = line[:] # deep copy line + return timings + +def parse_timing_file(file_path: str) -> list[Timing]: + timings: list[Timing] = [] + with open(file_path, 'r') as file: + for line in file: + seperator_idx = line.find(":") + if seperator_idx == -1: + continue + test_name = line[:seperator_idx] + try: + time = float(line[seperator_idx+2:-2]) + print(time) + except: + print("Couldn't convert: " + line[seperator_idx:-2] + " to a float") + continue + timings.append(Timing(time, test_name)) + + + return timings + +def save_timings(timings: list[Timing], file_path: str): + with open(file_path, 'w') as file: + for timing in timings: + file.write(f"{timing.to_string()}\n") + +parse_file_path = "matrix-test-timings-temp.txt" +save_file_path = "matrix-test-timings.txt" + +# get the new timings +new_timings = parse_test_file(parse_file_path) + +# get the old timings +old_timings = parse_timing_file(save_file_path) + +difference_increased = "" +# calculate the timing difference +for new_timing in new_timings: + for old_timing in old_timings: + if new_timing == old_timing: + new_timing.difference = new_timing - old_timing + if new_timing.difference >= 0.03: + difference_increased += f"{new_timing.test_name}, " + +def save_option(): + # save the new timings + while True: + option = input("Save Results? (y/n)") + if option[0].lower() == 'y': + save_timings(new_timings, save_file_path) + print("Saved.") + break + elif option[0].lower() == 'n': + break + +# print the new timing results along with the difference +for timing in new_timings: + print(timing.to_string_w_diff()) + +if len(difference_increased) > 0: + print("You increased the time it takes to run for:" + difference_increased) + save_option() +else: + print("No times have changed outside the margin of error.")