From 7feed2bd25beb287f50d62ef9c43a53c86e5f55b Mon Sep 17 00:00:00 2001 From: GorogPeter Date: Thu, 7 Sep 2023 11:13:08 +0200 Subject: [PATCH] Add Mandelbrot SIMD test to the benchmark tests --- test/wasmBenchmarker/benchmark.py | 20 +++-- test/wasmBenchmarker/ctests/mandelbrot.c | 84 +++++++++++--------- test/wasmBenchmarker/ctests/simdMandelbrot.c | 70 ++++++++++++++++ 3 files changed, 131 insertions(+), 43 deletions(-) create mode 100644 test/wasmBenchmarker/ctests/simdMandelbrot.c diff --git a/test/wasmBenchmarker/benchmark.py b/test/wasmBenchmarker/benchmark.py index 29bdc823f..5df803f88 100755 --- a/test/wasmBenchmarker/benchmark.py +++ b/test/wasmBenchmarker/benchmark.py @@ -36,17 +36,20 @@ "heapsort": 0, "huffman": 0, "k_nucleotide": 1, - "mandelbrot": 2091942736, + "mandelbrot": 775007, "nbody": -0.169083713, "nqueens": 0, "prime": 48611, "quick_sort": 0, "red-black": 4000000, - "salesman": 840 + "salesman": 840, + "simdMandelbrot": 775007 } # https://benchmarksgame-team.pages.debian.net/benchmarksgame/description/simple.html#simple gameTests = ["mandelbrot", "nbody", "gregory", "fannkuch", "k_nucleotide"] +simdTests = ["simdMandelbrot"] + def prepare_arg_pars(): parser = argparse.ArgumentParser() parser.add_argument('--test-dir', metavar='PATH', help='path to the test files written in c', nargs='?', @@ -58,6 +61,7 @@ def prepare_arg_pars(): parser.add_argument('--iterations', metavar='NUMBER', help='how many times run the tests', nargs='?', const='10', default=10, type=int) parser.add_argument('--compile-anyway', help='compile the tests even if they are compiled', action='store_true') + parser.add_argument('--enable-simd', help='run SIMD tests too', action='store_true') return parser.parse_args() @@ -98,7 +102,7 @@ def get_emcc(): return emcc_path -def compile_tests(emcc_path, path, only_game=False, compile_anyway=False, run=None): +def compile_tests(emcc_path, path, only_game=False, compile_anyway=False, simd=False, run=None): if not os.path.exists(emcc_path): print("invalid path for emcc: " + emcc_path) exit(1) @@ -118,7 +122,9 @@ def compile_tests(emcc_path, path, only_game=False, compile_anyway=False, run=No for file in test_list: name = file.split('.')[0] - if (name not in gameTests and only_game) or (run is not None and name != run): + if (name not in gameTests and only_game) or \ + (run is not None and name != run) or \ + (name in simdTests and not simd): continue test_names.append(name) @@ -127,8 +133,10 @@ def compile_tests(emcc_path, path, only_game=False, compile_anyway=False, run=No print("target files are found; compilation skipped") continue + extraFlags = "-msimd128" if file.startswith("simd") else "" + print("compiling " + name) - bob_the_stringbuilder = emcc_path + " " + path + "/" + file + " --no-entry -s WASM=1 -s EXPORTED_FUNCTIONS=_runtime -s EXPORTED_RUNTIME_METHODS=ccall,cwrap -o " + path + "/wasm/" + name + ".wasm" + bob_the_stringbuilder = emcc_path + " " + path + "/" + file + " " + extraFlags + " --no-entry -s WASM=1 -s EXPORTED_FUNCTIONS=_runtime -s EXPORTED_RUNTIME_METHODS=ccall,cwrap -o " + path + "/wasm/" + name + ".wasm" print(bob_the_stringbuilder) os.system(bob_the_stringbuilder) @@ -217,7 +225,7 @@ def main(): check_programs(args.engines) emcc_path = get_emcc() - test_names = compile_tests(emcc_path, args.test_dir, args.only_game, args.compile_anyway, args.run) + test_names = compile_tests(emcc_path, args.test_dir, args.only_game, args.compile_anyway, args.enable_simd, args.run) generate_report( run_tests(args.test_dir, test_names, args.engines, args.iterations), args.report) diff --git a/test/wasmBenchmarker/ctests/mandelbrot.c b/test/wasmBenchmarker/ctests/mandelbrot.c index 5ce52f081..7b0d91c29 100755 --- a/test/wasmBenchmarker/ctests/mandelbrot.c +++ b/test/wasmBenchmarker/ctests/mandelbrot.c @@ -14,57 +14,67 @@ * limitations under the License. */ -#include #include +#include +#include +#include -#define LOOP 600 -#define X_MIN -1.5 -#define X_MAX 0.5 -#define Y_MIN -1.0 -#define Y_MAX 1.0 -#define X_RES 512 +#define WIDTH 1600 +#define HIGHT 1400 +#define N 20 +#define REAL_AXIS_SHIFT -1.8 // ~ horizontal shift +#define IMAGINARY_AXIS_SHIFT -1.0 // ~ vertical shift +#define ZOOM 0.0015 -uint32_t runtime() { - const int yRes = (X_RES * (Y_MAX - Y_MIN)) / (X_MAX - X_MIN); +#define getNthBit(b, n) ((b & (1 << (7 - n))) > 0) - double dx = (X_MAX - X_MIN) / X_RES; - double dy = (Y_MAX - Y_MIN) / yRes; - double x, y; - int k; +#define clearNthBit(b, n) b = b & (0xFF - (1 << (7 - n))) - uint16_t retValLower = 0; - uint16_t retValHigher = 0; +#define setNthBit(b, n) b = b | (1 << (7 - n)) - for (int j = 0; j < yRes; j++) { - y = Y_MAX - j * dy; +#define ABS_COMPLEX(z_real, z_complex) (sqrtf(z_real * z_real + z_complex * z_complex)) - for (int i = 0; i < X_RES; i++) { - double u = 0; - double v = 0; - double u2 = 0; - double v2 = 0; +typedef uint8_t byte; - x = X_MIN + i * dx; +byte isInMandelbrotSet(float c_real, float c_imaginary) +{ + byte result = 0b10000000; + float z_real = 0; + float z_imaginary = 0; + for (size_t k = 0; k < N; k++) { + float complex_abs = ABS_COMPLEX(z_real, z_imaginary); + if (getNthBit(result, 0) == 1 && complex_abs > 2) { + clearNthBit(result, 0); + } else { + float next_z_real = (z_real * z_real - z_imaginary * z_imaginary) + c_real; + float next_z_imaginary = ((float)2.0 * z_real * z_imaginary) + c_imaginary; + z_real = next_z_real; + z_imaginary = next_z_imaginary; + } - for (k = 1; k < LOOP && (u2 + v2 < 4.0); k++) { - v = 2 * u * v + y; - u = u2 - v2 + x; - u2 = u * u; - v2 = v * v; - } + if (result == 0) { + break; + } + } + return result; +} - if (k >= LOOP) { - retValLower++; - } else { - retValHigher++; +uint32_t runtime() { + uint32_t setSize = 0; + for (int i = 0; i < HIGHT; i++) { + for (int j = 0; j < WIDTH; j++) { + float real = ((float)j * (float)ZOOM) + (float)REAL_AXIS_SHIFT; + float imaginary = ((float)i * (float)ZOOM) + (float)IMAGINARY_AXIS_SHIFT; + if (getNthBit(isInMandelbrotSet(real, imaginary),0)) { + setSize++; } } } - - return retValLower + (retValHigher << 16); + return setSize; } + int main() { - printf("%d\n", runtime()); + printf("%u\n", runtime()); return 0; -} \ No newline at end of file +} diff --git a/test/wasmBenchmarker/ctests/simdMandelbrot.c b/test/wasmBenchmarker/ctests/simdMandelbrot.c new file mode 100644 index 000000000..8399aab83 --- /dev/null +++ b/test/wasmBenchmarker/ctests/simdMandelbrot.c @@ -0,0 +1,70 @@ +#include +#include +#include + +#include + +#define WIDTH 1600 +#define HIGHT 1400 +#define N 20 +#define REAL_AXIS_SHIFT -1.8 // ~ horizontal shift +#define IMAGINARY_AXIS_SHIFT -1.0 // ~ vertical shift +#define ZOOM 0.0015 + +#define getNthBit(b, n) ((b & (1 << (7 - n))) > 0) + +#define clearNthBit(b, n) b = b & (0xFF - (1 << (7 - n))) + +#define setNthBit(b, n) b = b | (1 << (7 - n)) + +#define SQUARE(z) wasm_f32x4_mul(z, z) + +#define ABS_COMPLEX(z_real, z_complex) wasm_f32x4_sqrt(wasm_f32x4_add(SQUARE(z_real), SQUARE(z_imaginary))) + +typedef uint8_t byte; + +byte areInMandelbrotSet(v128_t c_real, v128_t c_imaginary) +{ + byte result = 0b11110000; + v128_t z_real = wasm_f32x4_const_splat(0); + v128_t z_imaginary = wasm_f32x4_const_splat(0); + for (size_t k = 0; k < N; k++) { + v128_t cmp_result = wasm_f32x4_gt(ABS_COMPLEX(z_real, z_imaginary), wasm_f32x4_const_splat(2)); + for (size_t i = 0; i < 4; i++) { + if (getNthBit(result, i) == 1 && ((float*)&cmp_result)[i] != 0) { + clearNthBit(result, i); + } + } + v128_t next_z_real = wasm_f32x4_add(wasm_f32x4_sub(SQUARE(z_real), SQUARE(z_imaginary)), c_real); + v128_t next_z_imaginary = wasm_f32x4_add(wasm_f32x4_mul(wasm_f32x4_mul(z_real, z_imaginary), wasm_f32x4_const_splat(2)), c_imaginary); + z_real = next_z_real; + z_imaginary = next_z_imaginary; + + if (result == 0) { + break; + } + } + return result; +} + +uint32_t runtime() { + uint32_t setSize = 0; + for (int i = 0; i < HIGHT; i++) { + for (int j = 0; j < WIDTH; j+=4) { + v128_t real = wasm_f32x4_add(wasm_f32x4_mul(wasm_f32x4_make(j, j+1, j+2, j+3), wasm_f32x4_const_splat(ZOOM)), wasm_f32x4_const_splat(REAL_AXIS_SHIFT)); + v128_t imaginary = wasm_f32x4_add(wasm_f32x4_mul(wasm_f32x4_make(i, i, i, i), wasm_f32x4_const_splat(ZOOM)), wasm_f32x4_const_splat(IMAGINARY_AXIS_SHIFT)); + byte pixels = areInMandelbrotSet(real, imaginary); + for (int i = 0; i < 4; i++) { + if (getNthBit(pixels, i)) { + setSize++; + } + } + } + } + return setSize; +} + +int main() { + printf("%u\n", runtime()); + return 0; +}