From nobody Fri Nov 7 02:19:10 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=linaro.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1544796150067629.6510659311006; Fri, 14 Dec 2018 06:02:30 -0800 (PST) Received: from localhost ([::1]:33692 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gXo2u-0006X4-SO for importer@patchew.org; Fri, 14 Dec 2018 09:02:28 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49205) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gXnvk-0008N7-Mt for qemu-devel@nongnu.org; Fri, 14 Dec 2018 08:55:06 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gXnvi-0001xm-Kp for qemu-devel@nongnu.org; Fri, 14 Dec 2018 08:55:04 -0500 Received: from mail-wr1-x42b.google.com ([2a00:1450:4864:20::42b]:32779) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gXnvi-0001xA-7a for qemu-devel@nongnu.org; Fri, 14 Dec 2018 08:55:02 -0500 Received: by mail-wr1-x42b.google.com with SMTP id c14so5550057wrr.0 for ; Fri, 14 Dec 2018 05:55:02 -0800 (PST) Received: from zen.linaro.local ([81.128.185.34]) by smtp.gmail.com with ESMTPSA id w125sm4737081wmb.45.2018.12.14.05.54.54 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 14 Dec 2018 05:54:56 -0800 (PST) Received: from zen.linaroharston (localhost [127.0.0.1]) by zen.linaro.local (Postfix) with ESMTP id A0CD53E0568; Fri, 14 Dec 2018 13:54:53 +0000 (GMT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=PiJJ8sQDJq1td+Nwr/8t517GlXzzi+KfjNDfMe1+2Sc=; b=ZiLoOA3180HJIvjI3pR2Ze6IJ2A1Q0LSnlXJaHg5PQ2aSZUPKXbN1nH5a5OZm62s6k gG8UZI8GYD9WfCybJE05WZB4DJ97DfI6G5e/j+J2Zbk8RS1BPt+WnX2gXthvuDzoX+hX YatvXpW0mDuV1ZmeRaFEsSjvD/0FOaJk8oL3c= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=PiJJ8sQDJq1td+Nwr/8t517GlXzzi+KfjNDfMe1+2Sc=; b=sDtioSG+0wrifp1/MNnq0QTSXB9sYnqAec228iRKSjVOptOeeXJpBJS6pToWxgsi4P B5MsDSy27Ww6VuODseXEkXTKZqxO0iZx01KUVHZEzrdhKIzYcsB6geL9gDa18cYfqk3T xH5Ge+yCPUftrbW9byLT51ynQKAuOjrI8VmskYmw6g35Rp5ry6mDWVYdlZKfte0zLo7V qhhA3l33uKqe0hz9sy/rDZwfNKufqLh5Y31K1QJOHYtwZfL27RTJC8A//OfG6jY6IWAW X4MHPD25lCNqjIdvFAi7J8nH2ZqoCj0up/O0BttKNIo5Ll03TOi7Ol2SKdojBCw2T7fQ Xvlg== X-Gm-Message-State: AA+aEWYJQflekyJuQ9OgKHiu8m/Q1BG6f0mTA7PBEEz8qqxOp/loBG3q 3KDUbzShg14u7mMdusCukSdV9A== X-Google-Smtp-Source: AFSGD/VIKw8kGrJldbE3T9hcjGJPWAszLB/6msPyC95jBOgJYfKjTsWRGl0ZkABI1JHUHy7oTYfEtQ== X-Received: by 2002:adf:d089:: with SMTP id y9mr2810032wrh.22.1544795701048; Fri, 14 Dec 2018 05:55:01 -0800 (PST) From: =?UTF-8?q?Alex=20Benn=C3=A9e?= To: peter.maydell@linaro.org Date: Fri, 14 Dec 2018 13:54:45 +0000 Message-Id: <20181214135452.25936-9-alex.bennee@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181214135452.25936-1-alex.bennee@linaro.org> References: <20181214135452.25936-1-alex.bennee@linaro.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::42b Subject: [Qemu-devel] [PULL 08/15] tests/fp: add fp-bench X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Emilio G. Cota" , =?UTF-8?q?Alex=20Benn=C3=A9e?= , qemu-devel@nongnu.org, Aurelien Jarno Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) From: "Emilio G. Cota" These microbenchmarks will allow us to measure the performance impact of FP emulation optimizations. Note that we can measure both directly the impa= ct on the softfloat functions (with "-t soft"), or the impact on an emulated workload (call with "-t host" and run under qemu user-mode). Reviewed-by: Alex Benn=C3=A9e Signed-off-by: Emilio G. Cota Signed-off-by: Alex Benn=C3=A9e diff --git a/tests/fp/.gitignore b/tests/fp/.gitignore index 8d45d18ac4..704fd42992 100644 --- a/tests/fp/.gitignore +++ b/tests/fp/.gitignore @@ -1 +1,2 @@ fp-test +fp-bench diff --git a/tests/fp/Makefile b/tests/fp/Makefile index 49cdcd1bd2..5019dcdca0 100644 --- a/tests/fp/Makefile +++ b/tests/fp/Makefile @@ -553,7 +553,7 @@ TF_OBJS_LIB +=3D $(TF_OBJS_WRITECASE) TF_OBJS_LIB +=3D testLoops_common.o TF_OBJS_LIB +=3D $(TF_OBJS_TEST) =20 -BINARIES :=3D fp-test$(EXESUF) +BINARIES :=3D fp-test$(EXESUF) fp-bench$(EXESUF) =20 # everything depends on config-host.h because platform.h includes it all: $(BUILD_DIR)/config-host.h @@ -590,10 +590,13 @@ $(TF_OBJS_LIB) slowfloat.o: %.o: $(TF_SOURCE_DIR)/%.c =20 libtestfloat.a: $(TF_OBJS_LIB) =20 +fp-bench$(EXESUF): fp-bench.o $(QEMU_SOFTFLOAT_OBJ) $(LIBQEMUUTIL) + clean: rm -f *.o *.d $(BINARIES) rm -f *.gcno *.gcda *.gcov rm -f fp-test$(EXESUF) + rm -f fp-bench$(EXESUF) rm -f libsoftfloat.a rm -f libtestfloat.a =20 diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c new file mode 100644 index 0000000000..f5bc5edebf --- /dev/null +++ b/tests/fp/fp-bench.c @@ -0,0 +1,630 @@ +/* + * fp-bench.c - A collection of simple floating point microbenchmarks. + * + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef HW_POISON_H +#error Must define HW_POISON_H to work around TARGET_* poisoning +#endif + +#include "qemu/osdep.h" +#include +#include +#include "qemu/timer.h" +#include "fpu/softfloat.h" + +/* amortize the computation of random inputs */ +#define OPS_PER_ITER 50000 + +#define MAX_OPERANDS 3 + +#define SEED_A 0xdeadfacedeadface +#define SEED_B 0xbadc0feebadc0fee +#define SEED_C 0xbeefdeadbeefdead + +enum op { + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_FMA, + OP_SQRT, + OP_CMP, + OP_MAX_NR, +}; + +static const char * const op_names[] =3D { + [OP_ADD] =3D "add", + [OP_SUB] =3D "sub", + [OP_MUL] =3D "mul", + [OP_DIV] =3D "div", + [OP_FMA] =3D "mulAdd", + [OP_SQRT] =3D "sqrt", + [OP_CMP] =3D "cmp", + [OP_MAX_NR] =3D NULL, +}; + +enum precision { + PREC_SINGLE, + PREC_DOUBLE, + PREC_FLOAT32, + PREC_FLOAT64, + PREC_MAX_NR, +}; + +enum rounding { + ROUND_EVEN, + ROUND_ZERO, + ROUND_DOWN, + ROUND_UP, + ROUND_TIEAWAY, + N_ROUND_MODES, +}; + +static const char * const round_names[] =3D { + [ROUND_EVEN] =3D "even", + [ROUND_ZERO] =3D "zero", + [ROUND_DOWN] =3D "down", + [ROUND_UP] =3D "up", + [ROUND_TIEAWAY] =3D "tieaway", +}; + +enum tester { + TESTER_SOFT, + TESTER_HOST, + TESTER_MAX_NR, +}; + +static const char * const tester_names[] =3D { + [TESTER_SOFT] =3D "soft", + [TESTER_HOST] =3D "host", + [TESTER_MAX_NR] =3D NULL, +}; + +union fp { + float f; + double d; + float32 f32; + float64 f64; + uint64_t u64; +}; + +struct op_state; + +typedef float (*float_func_t)(const struct op_state *s); +typedef double (*double_func_t)(const struct op_state *s); + +union fp_func { + float_func_t float_func; + double_func_t double_func; +}; + +typedef void (*bench_func_t)(void); + +struct op_desc { + const char * const name; +}; + +#define DEFAULT_DURATION_SECS 1 + +static uint64_t random_ops[MAX_OPERANDS] =3D { + SEED_A, SEED_B, SEED_C, +}; +static float_status soft_status; +static enum precision precision; +static enum op operation; +static enum tester tester; +static uint64_t n_completed_ops; +static unsigned int duration =3D DEFAULT_DURATION_SECS; +static int64_t ns_elapsed; +/* disable optimizations with volatile */ +static volatile union fp res; + +/* + * From: https://en.wikipedia.org/wiki/Xorshift + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is o= nly + * guaranteed to be >=3D INT_MAX). + */ +static uint64_t xorshift64star(uint64_t x) +{ + x ^=3D x >> 12; /* a */ + x ^=3D x << 25; /* b */ + x ^=3D x >> 27; /* c */ + return x * UINT64_C(2685821657736338717); +} + +static void update_random_ops(int n_ops, enum precision prec) +{ + int i; + + for (i =3D 0; i < n_ops; i++) { + uint64_t r =3D random_ops[i]; + + if (prec =3D=3D PREC_SINGLE || PREC_FLOAT32) { + do { + r =3D xorshift64star(r); + } while (!float32_is_normal(r)); + } else if (prec =3D=3D PREC_DOUBLE || PREC_FLOAT64) { + do { + r =3D xorshift64star(r); + } while (!float64_is_normal(r)); + } else { + g_assert_not_reached(); + } + random_ops[i] =3D r; + } +} + +static void fill_random(union fp *ops, int n_ops, enum precision prec, + bool no_neg) +{ + int i; + + for (i =3D 0; i < n_ops; i++) { + switch (prec) { + case PREC_SINGLE: + case PREC_FLOAT32: + ops[i].f32 =3D make_float32(random_ops[i]); + if (no_neg && float32_is_neg(ops[i].f32)) { + ops[i].f32 =3D float32_chs(ops[i].f32); + } + /* raise the exponent to limit the frequency of denormal resul= ts */ + ops[i].f32 |=3D 0x40000000; + break; + case PREC_DOUBLE: + case PREC_FLOAT64: + ops[i].f64 =3D make_float64(random_ops[i]); + if (no_neg && float64_is_neg(ops[i].f64)) { + ops[i].f64 =3D float64_chs(ops[i].f64); + } + /* raise the exponent to limit the frequency of denormal resul= ts */ + ops[i].f64 |=3D LIT64(0x4000000000000000); + break; + default: + g_assert_not_reached(); + } + } +} + +/* + * The main benchmark function. Instead of (ab)using macros, we rely + * on the compiler to unfold this at compile-time. + */ +static void bench(enum precision prec, enum op op, int n_ops, bool no_neg) +{ + int64_t tf =3D get_clock() + duration * 1000000000LL; + + while (get_clock() < tf) { + union fp ops[MAX_OPERANDS]; + int64_t t0; + int i; + + update_random_ops(n_ops, prec); + switch (prec) { + case PREC_SINGLE: + fill_random(ops, n_ops, prec, no_neg); + t0 =3D get_clock(); + for (i =3D 0; i < OPS_PER_ITER; i++) { + float a =3D ops[0].f; + float b =3D ops[1].f; + float c =3D ops[2].f; + + switch (op) { + case OP_ADD: + res.f =3D a + b; + break; + case OP_SUB: + res.f =3D a - b; + break; + case OP_MUL: + res.f =3D a * b; + break; + case OP_DIV: + res.f =3D a / b; + break; + case OP_FMA: + res.f =3D fmaf(a, b, c); + break; + case OP_SQRT: + res.f =3D sqrtf(a); + break; + case OP_CMP: + res.u64 =3D isgreater(a, b); + break; + default: + g_assert_not_reached(); + } + } + break; + case PREC_DOUBLE: + fill_random(ops, n_ops, prec, no_neg); + t0 =3D get_clock(); + for (i =3D 0; i < OPS_PER_ITER; i++) { + double a =3D ops[0].d; + double b =3D ops[1].d; + double c =3D ops[2].d; + + switch (op) { + case OP_ADD: + res.d =3D a + b; + break; + case OP_SUB: + res.d =3D a - b; + break; + case OP_MUL: + res.d =3D a * b; + break; + case OP_DIV: + res.d =3D a / b; + break; + case OP_FMA: + res.d =3D fma(a, b, c); + break; + case OP_SQRT: + res.d =3D sqrt(a); + break; + case OP_CMP: + res.u64 =3D isgreater(a, b); + break; + default: + g_assert_not_reached(); + } + } + break; + case PREC_FLOAT32: + fill_random(ops, n_ops, prec, no_neg); + t0 =3D get_clock(); + for (i =3D 0; i < OPS_PER_ITER; i++) { + float32 a =3D ops[0].f32; + float32 b =3D ops[1].f32; + float32 c =3D ops[2].f32; + + switch (op) { + case OP_ADD: + res.f32 =3D float32_add(a, b, &soft_status); + break; + case OP_SUB: + res.f32 =3D float32_sub(a, b, &soft_status); + break; + case OP_MUL: + res.f =3D float32_mul(a, b, &soft_status); + break; + case OP_DIV: + res.f32 =3D float32_div(a, b, &soft_status); + break; + case OP_FMA: + res.f32 =3D float32_muladd(a, b, c, 0, &soft_status); + break; + case OP_SQRT: + res.f32 =3D float32_sqrt(a, &soft_status); + break; + case OP_CMP: + res.u64 =3D float32_compare_quiet(a, b, &soft_status); + break; + default: + g_assert_not_reached(); + } + } + break; + case PREC_FLOAT64: + fill_random(ops, n_ops, prec, no_neg); + t0 =3D get_clock(); + for (i =3D 0; i < OPS_PER_ITER; i++) { + float64 a =3D ops[0].f64; + float64 b =3D ops[1].f64; + float64 c =3D ops[2].f64; + + switch (op) { + case OP_ADD: + res.f64 =3D float64_add(a, b, &soft_status); + break; + case OP_SUB: + res.f64 =3D float64_sub(a, b, &soft_status); + break; + case OP_MUL: + res.f =3D float64_mul(a, b, &soft_status); + break; + case OP_DIV: + res.f64 =3D float64_div(a, b, &soft_status); + break; + case OP_FMA: + res.f64 =3D float64_muladd(a, b, c, 0, &soft_status); + break; + case OP_SQRT: + res.f64 =3D float64_sqrt(a, &soft_status); + break; + case OP_CMP: + res.u64 =3D float64_compare_quiet(a, b, &soft_status); + break; + default: + g_assert_not_reached(); + } + } + break; + default: + g_assert_not_reached(); + } + ns_elapsed +=3D get_clock() - t0; + n_completed_ops +=3D OPS_PER_ITER; + } +} + +#define GEN_BENCH(name, type, prec, op, n_ops) \ + static void __attribute__((flatten)) name(void) \ + { \ + bench(prec, op, n_ops, false); \ + } + +#define GEN_BENCH_NO_NEG(name, type, prec, op, n_ops) \ + static void __attribute__((flatten)) name(void) \ + { \ + bench(prec, op, n_ops, true); \ + } + +#define GEN_BENCH_ALL_TYPES(opname, op, n_ops) \ + GEN_BENCH(bench_ ## opname ## _float, float, PREC_SINGLE, op, n_ops) \ + GEN_BENCH(bench_ ## opname ## _double, double, PREC_DOUBLE, op, n_ops)= \ + GEN_BENCH(bench_ ## opname ## _float32, float32, PREC_FLOAT32, op, n_o= ps) \ + GEN_BENCH(bench_ ## opname ## _float64, float64, PREC_FLOAT64, op, n_o= ps) + +GEN_BENCH_ALL_TYPES(add, OP_ADD, 2) +GEN_BENCH_ALL_TYPES(sub, OP_SUB, 2) +GEN_BENCH_ALL_TYPES(mul, OP_MUL, 2) +GEN_BENCH_ALL_TYPES(div, OP_DIV, 2) +GEN_BENCH_ALL_TYPES(fma, OP_FMA, 3) +GEN_BENCH_ALL_TYPES(cmp, OP_CMP, 2) +#undef GEN_BENCH_ALL_TYPES + +#define GEN_BENCH_ALL_TYPES_NO_NEG(name, op, n) \ + GEN_BENCH_NO_NEG(bench_ ## name ## _float, float, PREC_SINGLE, op, n) \ + GEN_BENCH_NO_NEG(bench_ ## name ## _double, double, PREC_DOUBLE, op, n= ) \ + GEN_BENCH_NO_NEG(bench_ ## name ## _float32, float32, PREC_FLOAT32, op= , n) \ + GEN_BENCH_NO_NEG(bench_ ## name ## _float64, float64, PREC_FLOAT64, op= , n) + +GEN_BENCH_ALL_TYPES_NO_NEG(sqrt, OP_SQRT, 1) +#undef GEN_BENCH_ALL_TYPES_NO_NEG + +#undef GEN_BENCH_NO_NEG +#undef GEN_BENCH + +#define GEN_BENCH_FUNCS(opname, op) \ + [op] =3D { \ + [PREC_SINGLE] =3D bench_ ## opname ## _float, \ + [PREC_DOUBLE] =3D bench_ ## opname ## _double, \ + [PREC_FLOAT32] =3D bench_ ## opname ## _float32, \ + [PREC_FLOAT64] =3D bench_ ## opname ## _float64, \ + } + +static const bench_func_t bench_funcs[OP_MAX_NR][PREC_MAX_NR] =3D { + GEN_BENCH_FUNCS(add, OP_ADD), + GEN_BENCH_FUNCS(sub, OP_SUB), + GEN_BENCH_FUNCS(mul, OP_MUL), + GEN_BENCH_FUNCS(div, OP_DIV), + GEN_BENCH_FUNCS(fma, OP_FMA), + GEN_BENCH_FUNCS(sqrt, OP_SQRT), + GEN_BENCH_FUNCS(cmp, OP_CMP), +}; + +#undef GEN_BENCH_FUNCS + +static void run_bench(void) +{ + bench_func_t f; + + f =3D bench_funcs[operation][precision]; + g_assert(f); + f(); +} + +/* @arr must be NULL-terminated */ +static int find_name(const char * const *arr, const char *name) +{ + int i; + + for (i =3D 0; arr[i] !=3D NULL; i++) { + if (strcmp(name, arr[i]) =3D=3D 0) { + return i; + } + } + return -1; +} + +static void usage_complete(int argc, char *argv[]) +{ + gchar *op_list =3D g_strjoinv(", ", (gchar **)op_names); + gchar *tester_list =3D g_strjoinv(", ", (gchar **)tester_names); + + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -d =3D duration, in seconds. Default: %d\n", + DEFAULT_DURATION_SECS); + fprintf(stderr, " -h =3D show this help message.\n"); + fprintf(stderr, " -o =3D floating point operation (%s). Default: %s\n", + op_list, op_names[0]); + fprintf(stderr, " -p =3D floating point precision (single, double). " + "Default: single\n"); + fprintf(stderr, " -r =3D rounding mode (even, zero, down, up, tieaway)= . " + "Default: even\n"); + fprintf(stderr, " -t =3D tester (%s). Default: %s\n", + tester_list, tester_names[0]); + fprintf(stderr, " -z =3D flush inputs to zero (soft tester only). " + "Default: disabled\n"); + fprintf(stderr, " -Z =3D flush output to zero (soft tester only). " + "Default: disabled\n"); + + g_free(tester_list); + g_free(op_list); +} + +static int round_name_to_mode(const char *name) +{ + int i; + + for (i =3D 0; i < N_ROUND_MODES; i++) { + if (!strcmp(round_names[i], name)) { + return i; + } + } + return -1; +} + +static void QEMU_NORETURN die_host_rounding(enum rounding rounding) +{ + fprintf(stderr, "fatal: '%s' rounding not supported on this host\n", + round_names[rounding]); + exit(EXIT_FAILURE); +} + +static void set_host_precision(enum rounding rounding) +{ + int rhost; + + switch (rounding) { + case ROUND_EVEN: + rhost =3D FE_TONEAREST; + break; + case ROUND_ZERO: + rhost =3D FE_TOWARDZERO; + break; + case ROUND_DOWN: + rhost =3D FE_DOWNWARD; + break; + case ROUND_UP: + rhost =3D FE_UPWARD; + break; + case ROUND_TIEAWAY: + die_host_rounding(rounding); + return; + default: + g_assert_not_reached(); + } + + if (fesetround(rhost)) { + die_host_rounding(rounding); + } +} + +static void set_soft_precision(enum rounding rounding) +{ + signed char mode; + + switch (rounding) { + case ROUND_EVEN: + mode =3D float_round_nearest_even; + break; + case ROUND_ZERO: + mode =3D float_round_to_zero; + break; + case ROUND_DOWN: + mode =3D float_round_down; + break; + case ROUND_UP: + mode =3D float_round_up; + break; + case ROUND_TIEAWAY: + mode =3D float_round_ties_away; + break; + default: + g_assert_not_reached(); + } + soft_status.float_rounding_mode =3D mode; +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + int val; + int rounding =3D ROUND_EVEN; + + for (;;) { + c =3D getopt(argc, argv, "d:ho:p:r:t:zZ"); + if (c < 0) { + break; + } + switch (c) { + case 'd': + duration =3D atoi(optarg); + break; + case 'h': + usage_complete(argc, argv); + exit(EXIT_SUCCESS); + case 'o': + val =3D find_name(op_names, optarg); + if (val < 0) { + fprintf(stderr, "Unsupported op '%s'\n", optarg); + exit(EXIT_FAILURE); + } + operation =3D val; + break; + case 'p': + if (!strcmp(optarg, "single")) { + precision =3D PREC_SINGLE; + } else if (!strcmp(optarg, "double")) { + precision =3D PREC_DOUBLE; + } else { + fprintf(stderr, "Unsupported precision '%s'\n", optarg); + exit(EXIT_FAILURE); + } + break; + case 'r': + rounding =3D round_name_to_mode(optarg); + if (rounding < 0) { + fprintf(stderr, "fatal: invalid rounding mode '%s'\n", opt= arg); + exit(EXIT_FAILURE); + } + break; + case 't': + val =3D find_name(tester_names, optarg); + if (val < 0) { + fprintf(stderr, "Unsupported tester '%s'\n", optarg); + exit(EXIT_FAILURE); + } + tester =3D val; + break; + case 'z': + soft_status.flush_inputs_to_zero =3D 1; + break; + case 'Z': + soft_status.flush_to_zero =3D 1; + break; + } + } + + /* set precision and rounding mode based on the tester */ + switch (tester) { + case TESTER_HOST: + set_host_precision(rounding); + break; + case TESTER_SOFT: + set_soft_precision(rounding); + switch (precision) { + case PREC_SINGLE: + precision =3D PREC_FLOAT32; + break; + case PREC_DOUBLE: + precision =3D PREC_FLOAT64; + break; + default: + g_assert_not_reached(); + } + break; + default: + g_assert_not_reached(); + } +} + +static void pr_stats(void) +{ + printf("%.2f MFlops\n", (double)n_completed_ops / ns_elapsed * 1e3); +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + run_bench(); + pr_stats(); + return 0; +} --=20 2.17.1