summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/asmjit/asmjit-testing/bench/asmjit_bench_overhead.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/asmjit/asmjit-testing/bench/asmjit_bench_overhead.cpp')
-rw-r--r--3rdparty/asmjit/asmjit-testing/bench/asmjit_bench_overhead.cpp431
1 files changed, 431 insertions, 0 deletions
diff --git a/3rdparty/asmjit/asmjit-testing/bench/asmjit_bench_overhead.cpp b/3rdparty/asmjit/asmjit-testing/bench/asmjit_bench_overhead.cpp
new file mode 100644
index 00000000000..94ef13774af
--- /dev/null
+++ b/3rdparty/asmjit/asmjit-testing/bench/asmjit_bench_overhead.cpp
@@ -0,0 +1,431 @@
+#include <asmjit/host.h>
+
+#include <asmjit-testing/commons/asmjitutils.h>
+#include <asmjit-testing/commons/cmdline.h>
+#include <asmjit-testing/commons/performancetimer.h>
+
+#include <stdint.h>
+
+using namespace asmjit;
+
+static void print_app_info(size_t n) noexcept {
+ printf("AsmJit Benchmark Overhead v%u.%u.%u [Arch=%s] [Mode=%s]\n\n",
+ unsigned((ASMJIT_LIBRARY_VERSION >> 16) ),
+ unsigned((ASMJIT_LIBRARY_VERSION >> 8) & 0xFF),
+ unsigned((ASMJIT_LIBRARY_VERSION ) & 0xFF),
+ asmjit_arch_as_string(Arch::kHost),
+ asmjit_build_type()
+ );
+
+ printf("This benchmark was designed to benchmark the cost of initialization and\n"
+ "reset (or reinitialization) of CodeHolder and Emitters; and the cost of\n"
+ "moving a minimal assembled function to executable memory. Each output line\n"
+ "provides the following columns:\n"
+ "\n"
+ " - <Test> - test case name - either 'CodeHolder' only or an emitter\n"
+ " - Strategy - reusability strategy - whether init/reset or reinit is used\n"
+ " - Reuse Only - no code generation, no use of emitter except for init/reuse\n"
+ " - Func - function was assembled\n"
+ " - RA - function was compiled (registers allocated) (Compiler)\n"
+ " - Asm - function was finalized & serialized (Builder/Compiler)\n"
+ " - RT - function was added to JitRuntime and then removed from it\n"
+ "\n"
+ "Essentially the output provides an insight into the cost of reusing\n"
+ "CodeHolder and other emitters, and the cost of assembling, finalizing,\n"
+ "and moving the assembled code into executable memory by separating each\n"
+ "phase.\n\n"
+ );
+
+ printf("The number of iterations benchmarked: %zu (override by --count=n)\n", n);
+ printf("\n");
+}
+
+enum class AssemblerOp {
+ kNone,
+ kRT
+};
+
+enum class BuilderOp {
+ kNone,
+ kFinalize,
+ kFinalize_RT
+};
+
+enum class CompilerOp {
+ kNone,
+ kCompile,
+ kFinalize,
+ kFinalize_RT
+};
+
+#if !defined(ASMJIT_NO_JIT)
+
+class MyErrorHandler : public ErrorHandler {
+public:
+ void handle_error(asmjit::Error err, const char* message, asmjit::BaseEmitter* origin) override {
+ Support::maybe_unused(err, origin);
+ fprintf(stderr, "AsmJit error: %s\n", message);
+ }
+};
+
+enum class InitStrategy : uint32_t {
+ kInitReset,
+ kReinit
+};
+
+static inline void bench_codeholder(InitStrategy strategy, size_t count) {
+ JitRuntime rt;
+ CodeHolder code;
+
+ if (strategy == InitStrategy::kInitReset) {
+ for (size_t i = 0; i < count; i++) {
+ code.init(rt.environment());
+ code.reset();
+ }
+ }
+ else {
+ code.init(rt.environment());
+ for (size_t i = 0; i < count; i++) {
+ code.reinit();
+ }
+ }
+}
+
+#if ASMJIT_ARCH_X86 != 0 && !defined(ASMJIT_NO_X86)
+template<typename EmitterT>
+static ASMJIT_INLINE void emit_raw_func(EmitterT& emitter) {
+ emitter.mov(x86::eax, 0);
+ emitter.ret();
+}
+
+template<typename CompilerT>
+static ASMJIT_INLINE void compile_raw_func(CompilerT& cc) {
+ x86::Gp r = cc.new_gp32();
+ cc.mov(r, 0);
+ cc.ret(r);
+}
+#endif
+
+#if ASMJIT_ARCH_ARM == 64 && !defined(ASMJIT_NO_AARCH64)
+template<typename EmitterT>
+static ASMJIT_INLINE void emit_raw_func(EmitterT& emitter) {
+ emitter.mov(a64::w0, 0);
+ emitter.ret(a64::x30);
+}
+
+template<typename CompilerT>
+static ASMJIT_INLINE void compile_raw_func(CompilerT& cc) {
+ a64::Gp gp = cc.new_gp32();
+ cc.mov(gp, 0);
+ cc.ret(gp);
+}
+#endif
+
+#if defined(ASMJIT_HAS_HOST_BACKEND)
+template<typename AssemblerT>
+static inline void bench_assembler(InitStrategy strategy, size_t count) {
+ JitRuntime rt;
+ CodeHolder code;
+ AssemblerT a;
+ MyErrorHandler eh;
+
+ if (strategy == InitStrategy::kInitReset) {
+ for (size_t i = 0; i < count; i++) {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&a);
+ code.reset();
+ }
+ }
+ else {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&a);
+
+ for (size_t i = 0; i < count; i++) {
+ code.reinit();
+ }
+ }
+}
+
+template<typename AssemblerT, AssemblerOp op>
+static inline void bench_assembler_func(InitStrategy strategy, size_t count) {
+ JitRuntime rt;
+ CodeHolder code;
+ AssemblerT a;
+ MyErrorHandler eh;
+
+ if (strategy == InitStrategy::kInitReset) {
+ for (size_t i = 0; i < count; i++) {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&a);
+ emit_raw_func(a);
+
+ if constexpr (op == AssemblerOp::kRT) {
+ using Func = uint32_t(*)(void);
+ Func fn;
+ rt.add(&fn, &code);
+ rt.release(fn);
+ }
+
+ code.reset();
+ }
+ }
+ else {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&a);
+
+ for (size_t i = 0; i < count; i++) {
+ code.reinit();
+ emit_raw_func(a);
+
+ if constexpr (op == AssemblerOp::kRT) {
+ using Func = uint32_t(*)(void);
+ Func fn;
+ rt.add(&fn, &code);
+ rt.release(fn);
+ }
+ }
+ }
+}
+#endif
+
+#if defined(ASMJIT_HAS_HOST_BACKEND) && !defined(ASMJIT_NO_BUILDER)
+template<typename BuilderT>
+static inline void bench_builder(InitStrategy strategy, size_t count) {
+ JitRuntime rt;
+ CodeHolder code;
+ BuilderT b;
+ MyErrorHandler eh;
+
+ if (strategy == InitStrategy::kInitReset) {
+ for (size_t i = 0; i < count; i++) {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&b);
+ code.reset();
+ }
+ }
+ else {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&b);
+
+ for (size_t i = 0; i < count; i++) {
+ code.reinit();
+ }
+ }
+}
+
+template<typename BuilderT, BuilderOp op>
+static inline void bench_builder_func(InitStrategy strategy, size_t count) {
+ JitRuntime rt;
+ CodeHolder code;
+ BuilderT b;
+ MyErrorHandler eh;
+
+ if (strategy == InitStrategy::kInitReset) {
+ for (size_t i = 0; i < count; i++) {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&b);
+ emit_raw_func(b);
+
+ if constexpr (op >= BuilderOp::kFinalize) {
+ b.finalize();
+ if constexpr (op == BuilderOp::kFinalize_RT) {
+ using Func = uint32_t(*)(void);
+ Func fn;
+ rt.add(&fn, &code);
+ rt.release(fn);
+ }
+ }
+
+ code.reset();
+ }
+ }
+ else {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&b);
+
+ for (size_t i = 0; i < count; i++) {
+ code.reinit();
+ emit_raw_func(b);
+
+ if constexpr (op >= BuilderOp::kFinalize) {
+ b.finalize();
+ if constexpr (op == BuilderOp::kFinalize_RT) {
+ using Func = uint32_t(*)(void);
+ Func fn;
+ rt.add(&fn, &code);
+ rt.release(fn);
+ }
+ }
+ }
+ }
+}
+#endif // ASMJIT_HAS_HOST_BACKEND && !ASMJIT_NO_BUILDER
+
+#if defined(ASMJIT_HAS_HOST_BACKEND) && !defined(ASMJIT_NO_COMPILER)
+template<typename CompilerT>
+static inline void bench_compiler(InitStrategy strategy, size_t count) {
+ JitRuntime rt;
+ CodeHolder code;
+ CompilerT cc;
+ MyErrorHandler eh;
+
+ if (strategy == InitStrategy::kInitReset) {
+ for (size_t i = 0; i < count; i++) {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&cc);
+ code.reset();
+ }
+ }
+ else {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&cc);
+
+ for (size_t i = 0; i < count; i++) {
+ code.reinit();
+ }
+ }
+}
+
+template<typename CompilerT, CompilerOp op>
+static inline void bench_compiler_func(InitStrategy strategy, size_t count) {
+ JitRuntime rt;
+ CodeHolder code;
+ CompilerT cc;
+ MyErrorHandler eh;
+
+ if (strategy == InitStrategy::kInitReset) {
+ for (size_t i = 0; i < count; i++) {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&cc);
+
+ (void)cc.add_func(FuncSignature::build<uint32_t>());
+ compile_raw_func(cc);
+ cc.end_func();
+
+ if constexpr (op == CompilerOp::kCompile) {
+ cc.run_passes();
+ }
+ else if constexpr (op >= CompilerOp::kFinalize) {
+ cc.finalize();
+ if constexpr (op == CompilerOp::kFinalize_RT) {
+ using Func = uint32_t(*)(void);
+ Func fn;
+ rt.add(&fn, &code);
+ rt.release(fn);
+ }
+ }
+
+ code.reset();
+ }
+ }
+ else {
+ code.init(rt.environment());
+ code.set_error_handler(&eh);
+ code.attach(&cc);
+
+ for (size_t i = 0; i < count; i++) {
+ code.reinit();
+
+ (void)cc.add_func(FuncSignature::build<uint32_t>());
+ compile_raw_func(cc);
+ cc.end_func();
+
+ if constexpr (op == CompilerOp::kCompile) {
+ cc.run_passes();
+ }
+ else if constexpr (op >= CompilerOp::kFinalize) {
+ cc.finalize();
+ if constexpr (op == CompilerOp::kFinalize_RT) {
+ using Func = uint32_t(*)(void);
+ Func fn;
+ rt.add(&fn, &code);
+ rt.release(fn);
+ }
+ }
+ }
+ }
+}
+#endif // ASMJIT_HAS_HOST_BACKEND && !ASMJIT_NO_COMPILER
+
+template<typename Lambda>
+static inline void test_perf(const char* group, const char* params, InitStrategy strategy, size_t n, Lambda&& fn) {
+ PerformanceTimer timer;
+ const char* strategy_name = strategy == InitStrategy::kInitReset ? "init/reset" : "reinit";
+
+ timer.start();
+ fn(strategy, n);
+ timer.stop();
+
+ printf("| %-10s | %-10s | %-23s| %8.1f [ms] |\n", group, strategy_name, params, timer.duration());
+}
+
+static inline void test_perf_all(InitStrategy strategy, size_t n) {
+ using IS = InitStrategy;
+
+ const char frame[] = "+------------+------------+------------------------+---------------+\n";
+ const char header[] = "| Group | Strategy | Features Used | Time [ms] |\n";
+
+ printf(frame);
+ printf(header);
+ printf(frame);
+
+ test_perf("CodeHolder", "Reuse Only" , strategy, n, [](IS s, size_t n) { bench_codeholder(s, n); });
+
+#if defined(ASMJIT_HAS_HOST_BACKEND)
+ test_perf("Assembler ", "Reuse Only" , strategy, n, [](IS s, size_t n) { bench_assembler<host::Assembler>(s, n); });
+ test_perf("Assembler ", "Func" , strategy, n, [](IS s, size_t n) { bench_assembler_func<host::Assembler, AssemblerOp::kNone>(s, n); });
+ test_perf("Assembler ", "Func + RT" , strategy, n, [](IS s, size_t n) { bench_assembler_func<host::Assembler, AssemblerOp::kRT>(s, n); });
+#endif
+
+#if defined(ASMJIT_HAS_HOST_BACKEND) && !defined(ASMJIT_NO_BUILDER)
+ test_perf("Builder ", "Reuse Only" , strategy, n, [](IS s, size_t n) { bench_builder<host::Builder>(s, n); });
+ test_perf("Builder ", "Func" , strategy, n, [](IS s, size_t n) { bench_builder_func<host::Builder, BuilderOp::kNone>(s, n); });
+ test_perf("Builder ", "Func + Asm" , strategy, n, [](IS s, size_t n) { bench_builder_func<host::Builder, BuilderOp::kFinalize>(s, n); });
+ test_perf("Builder ", "Func + Asm + RT" , strategy, n, [](IS s, size_t n) { bench_builder_func<host::Builder, BuilderOp::kFinalize_RT>(s, n); });
+#endif
+
+#if defined(ASMJIT_HAS_HOST_BACKEND) && !defined(ASMJIT_NO_COMPILER)
+
+ test_perf("Compiler ", "Reuse Only" , strategy, n, [](IS s, size_t n) { bench_compiler<host::Compiler>(s, n); });
+ test_perf("Compiler ", "Func" , strategy, n, [](IS s, size_t n) { bench_compiler_func<host::Compiler, CompilerOp::kNone>(s, n); });
+ test_perf("Compiler ", "Func + RA" , strategy, n, [](IS s, size_t n) { bench_compiler_func<host::Compiler, CompilerOp::kCompile>(s, n); });
+ test_perf("Compiler ", "Func + RA + Asm" , strategy, n, [](IS s, size_t n) { bench_compiler_func<host::Compiler, CompilerOp::kFinalize>(s, n); });
+ test_perf("Compiler ", "Func + RA + Asm + RT", strategy, n, [](IS s, size_t n) { bench_compiler_func<host::Compiler, CompilerOp::kFinalize_RT>(s, n); });
+#endif
+
+ printf(frame);
+}
+
+int main(int argc, char* argv[]) {
+ CmdLine cmd_line(argc, argv);
+ size_t n = cmd_line.value_as_uint("--count", 1000000);
+
+ print_app_info(n);
+
+ test_perf_all(InitStrategy::kInitReset, n);
+ printf("\n");
+ test_perf_all(InitStrategy::kReinit, n);
+
+ return 0;
+}
+
+#else
+
+int main() {
+ print_app_info(0);
+ printf("!!AsmJit Benchmark Reuse is currently disabled: <ASMJIT_NO_JIT> or unsuitable target architecture !!\n");
+ return 0;
+}
+
+#endif