Skip to content

Commit

Permalink
Add Mandelbrot SIMD test to the benchmark tests
Browse files Browse the repository at this point in the history
  • Loading branch information
GorogPeter committed Oct 6, 2023
1 parent 708e231 commit 7feed2b
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 43 deletions.
20 changes: 14 additions & 6 deletions test/wasmBenchmarker/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -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='?',
Expand All @@ -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()


Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
84 changes: 47 additions & 37 deletions test/wasmBenchmarker/ctests/mandelbrot.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,57 +14,67 @@
* limitations under the License.
*/

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <math.h>
#include <stdint.h>

#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;
}
}
70 changes: 70 additions & 0 deletions test/wasmBenchmarker/ctests/simdMandelbrot.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>

#include <wasm_simd128.h>

#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;
}

0 comments on commit 7feed2b

Please sign in to comment.