From 882a3ef7e9042f7ecff3f6807d874054aa48e7eb 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 | 18 +++- .../ctests/simdMandelbrotCount.c | 91 +++++++++++++++++++ 2 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 test/wasmBenchmarker/ctests/simdMandelbrotCount.c diff --git a/test/wasmBenchmarker/benchmark.py b/test/wasmBenchmarker/benchmark.py index 29bdc823f..823b898b2 100755 --- a/test/wasmBenchmarker/benchmark.py +++ b/test/wasmBenchmarker/benchmark.py @@ -42,11 +42,14 @@ "prime": 48611, "quick_sort": 0, "red-black": 4000000, - "salesman": 840 + "salesman": 840, + "simdMandelbrotCount": 775007 } # https://benchmarksgame-team.pages.debian.net/benchmarksgame/description/simple.html#simple gameTests = ["mandelbrot", "nbody", "gregory", "fannkuch", "k_nucleotide"] +simdTests = ["simdMandelbrotCount"] + 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('--simd-tests', 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 = "-mavx -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.simd_tests, args.run) generate_report( run_tests(args.test_dir, test_names, args.engines, args.iterations), args.report) diff --git a/test/wasmBenchmarker/ctests/simdMandelbrotCount.c b/test/wasmBenchmarker/ctests/simdMandelbrotCount.c new file mode 100644 index 000000000..4f014f7b2 --- /dev/null +++ b/test/wasmBenchmarker/ctests/simdMandelbrotCount.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023-present Samsung Electronics Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include // SIMD header + +#define WIDTH 1600 // WIDTH has to be a multiple of four! +#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 & (0b00000001 << (7 - n))) > 0) + +#define clearNthBit(b, n) b = b & (0b11111111 - (0b00000001 << (7 - n))) + +#define setNthBit(b, n) b = b | (0b00000001 << (7 - n)) + +#define SQUARE(num) _mm_mul_ps(num, num) + +#define ABS_COMPLEX(z_real, z_complex) _mm_sqrt_ps(_mm_add_ps(SQUARE(z_real), SQUARE(z_imaginary))) + +typedef uint8_t byte; + + +byte areInMandelbrotSet(__m128 c_real, __m128 c_imaginary) { + byte result = 0b11110000; + __m128 z_real = _mm_set1_ps(0); + __m128 z_imaginary = _mm_set1_ps(0); + + for (size_t i = 0; i < N; i++) { + __m128 cmp_result = _mm_cmpgt_ps(ABS_COMPLEX(z_real, z_imaginary), _mm_set1_ps(2)); + for (size_t j = 0; j < 4; j++) { + if (getNthBit(result, j) == 1 && ((float*)&cmp_result)[j] != 0) { + clearNthBit(result, j); + } + } + + __m128 next_z_real = _mm_add_ps(_mm_sub_ps(SQUARE(z_real), SQUARE(z_imaginary)), c_real); + __m128 next_z_imaginary = _mm_add_ps(_mm_mul_ps(_mm_mul_ps(z_real, z_imaginary), _mm_set1_ps(2)), c_imaginary); + z_real = next_z_real; + z_imaginary = next_z_imaginary; + + if (result == 0b00000000) { + return result; + } + } + return result; +} + + +uint32_t runtime() { + uint32_t setSize = 0; + for (int i = 0; i < HIGHT; i++) { + for (int j = 0; j < WIDTH; j += 4) { + __m128 real = _mm_add_ps(_mm_mul_ps(_mm_set_ps(j + 3, j + 2, j + 1, j), _mm_set1_ps(ZOOM)), _mm_set1_ps(REAL_AXIS_SHIFT)); + __m128 imaginary = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(i), _mm_set1_ps(ZOOM)), _mm_set1_ps(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; +}