diff options
| -rw-r--r-- | Math_Ext.jai | 2 | ||||
| -rw-r--r-- | Math_Test.jai | 187 |
2 files changed, 107 insertions, 82 deletions
diff --git a/Math_Ext.jai b/Math_Ext.jai index ad41c2f..8f7531b 100644 --- a/Math_Ext.jai +++ b/Math_Ext.jai @@ -380,7 +380,7 @@ div :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, remainder: T mov limit: gpr, LIMIT; mov result, x; cmovz result, limit; // If ZF: limit dividend to MIN-1. - cbw result; + cbw result; // Sign-extension. setz saturated; idiv.SIZE result, y; diff --git a/Math_Test.jai b/Math_Test.jai index 8f3dce1..efc687c 100644 --- a/Math_Test.jai +++ b/Math_Test.jai @@ -8,7 +8,10 @@ main :: () { - write_strings("=====================\n", "--- Test Math_Ext ---\n"); + write_strings( + "#========================#\n", + "# Unit tests #\n" + ); test_op :: (operation: string, x: $Tx, y: $Ty, result: $Tr, type: Type, saturated: bool, remainder: Tr = 0) -> errors_found: int #expand { @@ -18,13 +21,13 @@ main :: () { if operation != "div" { TEST_CALL :: #string DONE t_result, t_saturated := OP(cast(Tx)x, cast(Ty)y); - print("%_%(%, %) = %0%0\n", operation, type, x, y, result, ifx saturated then " : saturated"); + if result != t_result print("%_%(%, %) = %0%0\n", operation, type, x, y, result, ifx saturated then " : saturated"); DONE str = replace(TEST_CALL, "OP", operation); } else { TEST_CALL :: #string DONE t_result, t_remainder, t_saturated := OP(cast(Tx)x, cast(Ty)y); - print("%_%(%, %) = % + %0%0\n", operation, type, x, y, result, remainder, ifx saturated then " : saturated"); + if result != t_result print("%_%(%, %) = % + %0%0\n", operation, type, x, y, result, remainder, ifx saturated then " : saturated"); DONE str = replace(TEST_CALL, "OP", operation); } @@ -155,15 +158,25 @@ main :: () { if errors > 0 print("# Found % %!\n", errors, ifx errors == 1 then "error" else "errors"); else print(" No errors found.\n"); - performance_test :: (operation: string, $type: Type) -> ops_size: s64, time_gen: Apollo_Time, time_asm: Apollo_Time #expand { + write_strings( + "#========================#\n", + "# Benchmarks #\n" + ); + + #import "Random"; + performance_test :: ($operation: string, $type: Type, print_result: bool = true) -> ops_per_us_gen: float, ops_per_us_asm: float { - SIZE :: 12000; + NUM_TESTS :: 500; + SIZE_DATA :: 12000; // Keep it below cache size. + best_gen := 0.0; + best_asm := 0.0; + numbers_x: [..] type; numbers_y: [..] type; - numbers_zgen: [SIZE] type; - numbers_zasm: [SIZE] type; - array_reserve(*numbers_x, SIZE); - array_reserve(*numbers_y, SIZE); + numbers_zgen: [SIZE_DATA] type; + numbers_zasm: [SIZE_DATA] type; + array_reserve(*numbers_x, SIZE_DATA); + array_reserve(*numbers_y, SIZE_DATA); #if type == u8 { MIN :: 0; MAX :: U8_MAX; } #if type == u16 { MIN :: 0; MAX :: U16_MAX; } @@ -173,9 +186,8 @@ main :: () { #if type == s16 { MIN :: S16_MIN; MAX :: S16_MAX; } #if type == s32 { MIN :: S32_MIN; MAX :: S32_MAX; } #if type == s64 { MIN :: S64_MIN; MAX :: S64_MAX; } - - for 0..SIZE-1 { - + + for 0..SIZE_DATA-1 { x := cast(type) random_get_within_range(xx MIN, xx MAX); y := cast(type) random_get_within_range(xx MIN, xx MAX); if y == 0 && operation == "div" { @@ -184,79 +196,92 @@ main :: () { array_add(*numbers_x, x); array_add(*numbers_y, y); } + + for 0..NUM_TESTS-1 { + + time_gen := current_time_monotonic(); + for 0..SIZE_DATA-1 #insert #run replace("numbers_zgen[it] = OP(numbers_x[it], numbers_y[it], true);", "OP", operation); + time_gen = current_time_monotonic() - time_gen; - control_gen: type; - control_asm: type; - - control_gen = 1; - time_gen := current_time_monotonic(); - for 0..SIZE-1 #insert #run replace("numbers_zgen[it] = OP(numbers_x[it], numbers_y[it], true);", "OP", operation); - time_gen = current_time_monotonic() - time_gen; - - control_asm = 1; - time_asm := current_time_monotonic(); - for 0..SIZE-1 #insert #run replace("numbers_zasm[it] = OP(numbers_x[it], numbers_y[it]);", "OP", operation); - time_asm = current_time_monotonic() - time_asm; + time_asm := current_time_monotonic(); + for 0..SIZE_DATA-1 #insert #run replace("numbers_zasm[it] = OP(numbers_x[it], numbers_y[it]);", "OP", operation); + time_asm = current_time_monotonic() - time_asm; - for 0..SIZE-1 assert(numbers_zgen[it] == numbers_zasm[it]); + for 0..SIZE_DATA-1 assert(numbers_zgen[it] == numbers_zasm[it]); - return SIZE, time_gen, time_asm; - } - - // Performance test. - #import "Random"; - best_gen: float; - best_asm: float; - - TEST_STR :: #string DONE - best_gen = 0; - best_asm = 0; - for 0..500 { - size, time_gen, time_asm := performance_test("OP", TYPE); - perf_gen := cast(float)size/cast(float)to_microseconds(time_gen); - perf_asm := cast(float)size/cast(float)to_microseconds(time_asm); + perf_gen := cast(float)SIZE_DATA/cast(float)to_microseconds(time_gen); + perf_asm := cast(float)SIZE_DATA/cast(float)to_microseconds(time_asm); best_gen = max(best_gen, perf_gen); best_asm = max(best_asm, perf_asm); } - print("% : %\n generic : %\n asm : %\n", "OP", TYPE, best_gen, best_asm); - DONE - - print("----- op : ops/usec ---\n"); - types :: string.["s8", "s16", "s32", "s64"]; - - #insert #run replace(replace(TEST_STR, "OP", "add"), "TYPE", "s8"); - #insert #run replace(replace(TEST_STR, "OP", "add"), "TYPE", "s16"); - #insert #run replace(replace(TEST_STR, "OP", "add"), "TYPE", "s32"); - #insert #run replace(replace(TEST_STR, "OP", "add"), "TYPE", "s64"); - #insert #run replace(replace(TEST_STR, "OP", "add"), "TYPE", "u8"); - #insert #run replace(replace(TEST_STR, "OP", "add"), "TYPE", "u16"); - #insert #run replace(replace(TEST_STR, "OP", "add"), "TYPE", "u32"); - #insert #run replace(replace(TEST_STR, "OP", "add"), "TYPE", "u64"); - - #insert #run replace(replace(TEST_STR, "OP", "sub"), "TYPE", "s8"); - #insert #run replace(replace(TEST_STR, "OP", "sub"), "TYPE", "s16"); - #insert #run replace(replace(TEST_STR, "OP", "sub"), "TYPE", "s32"); - #insert #run replace(replace(TEST_STR, "OP", "sub"), "TYPE", "s64"); - #insert #run replace(replace(TEST_STR, "OP", "sub"), "TYPE", "u8"); - #insert #run replace(replace(TEST_STR, "OP", "sub"), "TYPE", "u16"); - #insert #run replace(replace(TEST_STR, "OP", "sub"), "TYPE", "u32"); - #insert #run replace(replace(TEST_STR, "OP", "sub"), "TYPE", "u64"); - - #insert #run replace(replace(TEST_STR, "OP", "mul"), "TYPE", "s8"); - #insert #run replace(replace(TEST_STR, "OP", "mul"), "TYPE", "s16"); - #insert #run replace(replace(TEST_STR, "OP", "mul"), "TYPE", "s32"); - #insert #run replace(replace(TEST_STR, "OP", "mul"), "TYPE", "s64"); - #insert #run replace(replace(TEST_STR, "OP", "mul"), "TYPE", "u8"); - #insert #run replace(replace(TEST_STR, "OP", "mul"), "TYPE", "u16"); - #insert #run replace(replace(TEST_STR, "OP", "mul"), "TYPE", "u32"); - #insert #run replace(replace(TEST_STR, "OP", "mul"), "TYPE", "u64"); - - #insert #run replace(replace(TEST_STR, "OP", "div"), "TYPE", "s8"); - #insert #run replace(replace(TEST_STR, "OP", "div"), "TYPE", "s16"); - #insert #run replace(replace(TEST_STR, "OP", "div"), "TYPE", "s32"); - #insert #run replace(replace(TEST_STR, "OP", "div"), "TYPE", "s64"); - #insert #run replace(replace(TEST_STR, "OP", "div"), "TYPE", "u8"); - #insert #run replace(replace(TEST_STR, "OP", "div"), "TYPE", "u16"); - #insert #run replace(replace(TEST_STR, "OP", "div"), "TYPE", "u32"); - #insert #run replace(replace(TEST_STR, "OP", "div"), "TYPE", "u64"); + + if print_result { + if type == s8 || type == u8 write_string(" "); + print("% | % | % |\n", type, best_gen, best_asm); + } + return best_gen, best_asm; + } + + ff := context.print_style.default_format_float; + ff.zero_removal = .NO; + ff.width = 7; + ff.trailing_width = 1; + context.print_style.default_format_float = ff; + + write_strings( + " # (ops / usec) #\n", + " | generic | x64 asm |\n" + ); + + write_strings( + "--- | ----------------- |\n", + " | add |\n" + ); + performance_test("add", u8); + performance_test("add", u16); + performance_test("add", u32); + performance_test("add", u64); + performance_test("add", s8); + performance_test("add", s16); + performance_test("add", s32); + performance_test("add", s64); + + write_strings( + "--- | ----------------- |\n", + " | sub |\n" + ); + performance_test("sub", u8); + performance_test("sub", u16); + performance_test("sub", u32); + performance_test("sub", u64); + performance_test("sub", s8); + performance_test("sub", s16); + performance_test("sub", s32); + performance_test("sub", s64); + + write_strings( + "--- | ----------------- |\n", + " | mul |\n" + ); + performance_test("mul", u8); + performance_test("mul", u16); + performance_test("mul", u32); + performance_test("mul", u64); + performance_test("mul", s8); + performance_test("mul", s16); + performance_test("mul", s32); + performance_test("mul", s64); + + write_strings( + "--- | ----------------- |\n", + " | div |\n" + ); + performance_test("div", u8); + performance_test("div", u16); + performance_test("div", u32); + performance_test("div", u64); + performance_test("div", s8); + performance_test("div", s16); + performance_test("div", s32); + performance_test("div", s64); } |
