asmjit_bench_overhead.cpp (12511B)
1 #include <stdint.h> 2 #include <asmjit/host.h> 3 4 #include "../commons/asmjitutils.h" 5 #include "../commons/cmdline.h" 6 #include "../commons/performancetimer.h" 7 8 using namespace asmjit; 9 10 static void print_app_info(size_t n) noexcept { 11 printf("AsmJit Benchmark Overhead v%u.%u.%u [Arch=%s] [Mode=%s]\n\n", 12 unsigned((ASMJIT_LIBRARY_VERSION >> 16) ), 13 unsigned((ASMJIT_LIBRARY_VERSION >> 8) & 0xFF), 14 unsigned((ASMJIT_LIBRARY_VERSION ) & 0xFF), 15 asmjit_arch_as_string(Arch::kHost), 16 asmjit_build_type() 17 ); 18 19 printf("This benchmark was designed to benchmark the cost of initialization and\n" 20 "reset (or reinitialization) of CodeHolder and Emitters; and the cost of\n" 21 "moving a minimal assembled function to executable memory. Each output line\n" 22 "uses \"<Test> [Func] [Finalize] [RT]\" format, with the following meaning:\n" 23 "\n" 24 " - <Test> - test case name - either 'CodeHolder' only or an emitter\n" 25 " - [Func] - function was assembled\n" 26 " - [Finalize] - function was finalized (Builder/Compiler)\n" 27 " - [RT] - function was added to JitRuntime and then removed from it\n" 28 "\n" 29 "Essentially the output provides an insight into the cost of reusing\n" 30 "CodeHolder and other emitters, and the cost of assembling, finalizing,\n" 31 "and moving the assembled code into executable memory by separating each\n" 32 "phase.\n\n" 33 ); 34 35 printf("The number of iterations benchmarked: %zu (override by --count=n)\n", n); 36 printf("\n"); 37 } 38 39 #if !defined(ASMJIT_NO_JIT) 40 41 class MyErrorHandler : public ErrorHandler { 42 public: 43 void handle_error(asmjit::Error err, const char* message, asmjit::BaseEmitter* origin) override { 44 Support::maybe_unused(err, origin); 45 fprintf(stderr, "AsmJit error: %s\n", message); 46 } 47 }; 48 49 enum class InitStrategy : uint32_t { 50 kInitReset, 51 kReinit 52 }; 53 54 static inline void bench_codeholder(InitStrategy strategy, size_t count) { 55 JitRuntime rt; 56 CodeHolder code; 57 58 if (strategy == InitStrategy::kInitReset) { 59 for (size_t i = 0; i < count; i++) { 60 code.init(rt.environment()); 61 code.reset(); 62 } 63 } 64 else { 65 code.init(rt.environment()); 66 for (size_t i = 0; i < count; i++) { 67 code.reinit(); 68 } 69 } 70 } 71 72 #if ASMJIT_ARCH_X86 != 0 && !defined(ASMJIT_NO_X86) 73 template<typename EmitterT> 74 static ASMJIT_INLINE void emit_raw_func(EmitterT& emitter) { 75 emitter.mov(x86::eax, 0); 76 emitter.ret(); 77 } 78 79 template<typename CompilerT> 80 static ASMJIT_INLINE void compile_raw_func(CompilerT& cc) { 81 x86::Gp r = cc.new_gp32(); 82 cc.mov(r, 0); 83 cc.ret(r); 84 } 85 #endif 86 87 #if ASMJIT_ARCH_ARM == 64 && !defined(ASMJIT_NO_AARCH64) 88 template<typename EmitterT> 89 static ASMJIT_INLINE void emit_raw_func(EmitterT& emitter) { 90 emitter.mov(a64::w0, 0); 91 emitter.ret(a64::x30); 92 } 93 94 template<typename CompilerT> 95 static ASMJIT_INLINE void compile_raw_func(CompilerT& cc) { 96 a64::Gp gp = cc.new_gp32(); 97 cc.mov(gp, 0); 98 cc.ret(gp); 99 } 100 #endif 101 102 #if defined(ASMJIT_HAS_HOST_BACKEND) 103 template<typename AssemblerT> 104 static inline void bench_assembler(InitStrategy strategy, size_t count) { 105 JitRuntime rt; 106 CodeHolder code; 107 AssemblerT a; 108 MyErrorHandler eh; 109 110 if (strategy == InitStrategy::kInitReset) { 111 for (size_t i = 0; i < count; i++) { 112 code.init(rt.environment()); 113 code.set_error_handler(&eh); 114 code.attach(&a); 115 code.reset(); 116 } 117 } 118 else { 119 code.init(rt.environment()); 120 code.set_error_handler(&eh); 121 code.attach(&a); 122 123 for (size_t i = 0; i < count; i++) { 124 code.reinit(); 125 } 126 } 127 } 128 129 template<typename AssemblerT> 130 static inline void bench_assembler_func(InitStrategy strategy, size_t count) { 131 JitRuntime rt; 132 CodeHolder code; 133 AssemblerT a; 134 MyErrorHandler eh; 135 136 if (strategy == InitStrategy::kInitReset) { 137 for (size_t i = 0; i < count; i++) { 138 code.init(rt.environment()); 139 code.set_error_handler(&eh); 140 code.attach(&a); 141 emit_raw_func(a); 142 code.reset(); 143 } 144 } 145 else { 146 code.init(rt.environment()); 147 code.set_error_handler(&eh); 148 code.attach(&a); 149 150 for (size_t i = 0; i < count; i++) { 151 code.reinit(); 152 emit_raw_func(a); 153 } 154 } 155 } 156 157 template<typename AssemblerT> 158 static inline void bench_assembler_func_rt(InitStrategy strategy, size_t count) { 159 JitRuntime rt; 160 CodeHolder code; 161 AssemblerT a; 162 MyErrorHandler eh; 163 164 using Func = uint32_t(*)(void); 165 166 if (strategy == InitStrategy::kInitReset) { 167 for (size_t i = 0; i < count; i++) { 168 code.init(rt.environment()); 169 code.set_error_handler(&eh); 170 code.attach(&a); 171 emit_raw_func(a); 172 173 Func fn; 174 rt.add(&fn, &code); 175 rt.release(fn); 176 177 code.reset(); 178 } 179 } 180 else { 181 code.init(rt.environment()); 182 code.set_error_handler(&eh); 183 code.attach(&a); 184 185 for (size_t i = 0; i < count; i++) { 186 code.reinit(); 187 emit_raw_func(a); 188 189 Func fn; 190 rt.add(&fn, &code); 191 rt.release(fn); 192 } 193 } 194 } 195 #endif 196 197 #if defined(ASMJIT_HAS_HOST_BACKEND) && !defined(ASMJIT_NO_BUILDER) 198 template<typename BuilderT> 199 static inline void bench_builder(InitStrategy strategy, size_t count) { 200 JitRuntime rt; 201 CodeHolder code; 202 BuilderT b; 203 MyErrorHandler eh; 204 205 if (strategy == InitStrategy::kInitReset) { 206 for (size_t i = 0; i < count; i++) { 207 code.init(rt.environment()); 208 code.set_error_handler(&eh); 209 code.attach(&b); 210 code.reset(); 211 } 212 } 213 else { 214 code.init(rt.environment()); 215 code.set_error_handler(&eh); 216 code.attach(&b); 217 218 for (size_t i = 0; i < count; i++) { 219 code.reinit(); 220 } 221 } 222 } 223 224 template<typename BuilderT> 225 static inline void bench_builder_func(InitStrategy strategy, size_t count, bool finalize) { 226 JitRuntime rt; 227 CodeHolder code; 228 BuilderT b; 229 MyErrorHandler eh; 230 231 if (strategy == InitStrategy::kInitReset) { 232 for (size_t i = 0; i < count; i++) { 233 code.init(rt.environment()); 234 code.set_error_handler(&eh); 235 code.attach(&b); 236 emit_raw_func(b); 237 238 if (finalize) { 239 b.finalize(); 240 } 241 code.reset(); 242 } 243 } 244 else { 245 code.init(rt.environment()); 246 code.set_error_handler(&eh); 247 code.attach(&b); 248 249 for (size_t i = 0; i < count; i++) { 250 code.reinit(); 251 emit_raw_func(b); 252 253 if (finalize) { 254 b.finalize(); 255 } 256 } 257 } 258 } 259 260 template<typename BuilderT> 261 static inline void bench_builder_func_finalize_rt(InitStrategy strategy, size_t count) { 262 JitRuntime rt; 263 CodeHolder code; 264 BuilderT b; 265 MyErrorHandler eh; 266 267 using Func = uint32_t(*)(void); 268 269 if (strategy == InitStrategy::kInitReset) { 270 for (size_t i = 0; i < count; i++) { 271 code.init(rt.environment()); 272 code.set_error_handler(&eh); 273 code.attach(&b); 274 emit_raw_func(b); 275 b.finalize(); 276 277 Func fn; 278 rt.add(&fn, &code); 279 rt.release(fn); 280 281 code.reset(); 282 } 283 } 284 else { 285 code.init(rt.environment()); 286 code.set_error_handler(&eh); 287 code.attach(&b); 288 289 for (size_t i = 0; i < count; i++) { 290 code.reinit(); 291 emit_raw_func(b); 292 b.finalize(); 293 294 Func fn; 295 rt.add(&fn, &code); 296 rt.release(fn); 297 } 298 } 299 } 300 #endif // ASMJIT_HAS_HOST_BACKEND && !ASMJIT_NO_BUILDER 301 302 #if defined(ASMJIT_HAS_HOST_BACKEND) && !defined(ASMJIT_NO_COMPILER) 303 template<typename CompilerT> 304 static inline void bench_compiler(InitStrategy strategy, size_t count) { 305 JitRuntime rt; 306 CodeHolder code; 307 CompilerT cc; 308 MyErrorHandler eh; 309 310 if (strategy == InitStrategy::kInitReset) { 311 for (size_t i = 0; i < count; i++) { 312 code.init(rt.environment()); 313 code.set_error_handler(&eh); 314 code.attach(&cc); 315 code.reset(); 316 } 317 } 318 else { 319 code.init(rt.environment()); 320 code.set_error_handler(&eh); 321 code.attach(&cc); 322 323 for (size_t i = 0; i < count; i++) { 324 code.reinit(); 325 } 326 } 327 } 328 329 template<typename CompilerT> 330 static inline void bench_compiler_func(InitStrategy strategy, size_t count, bool finalize) { 331 JitRuntime rt; 332 CodeHolder code; 333 CompilerT cc; 334 MyErrorHandler eh; 335 336 if (strategy == InitStrategy::kInitReset) { 337 for (size_t i = 0; i < count; i++) { 338 code.init(rt.environment()); 339 code.set_error_handler(&eh); 340 code.attach(&cc); 341 342 (void)cc.add_func(FuncSignature::build<uint32_t>()); 343 compile_raw_func(cc); 344 cc.end_func(); 345 346 if (finalize) { 347 cc.finalize(); 348 } 349 350 code.reset(); 351 } 352 } 353 else { 354 code.init(rt.environment()); 355 code.set_error_handler(&eh); 356 code.attach(&cc); 357 358 for (size_t i = 0; i < count; i++) { 359 code.reinit(); 360 361 (void)cc.add_func(FuncSignature::build<uint32_t>()); 362 compile_raw_func(cc); 363 cc.end_func(); 364 365 if (finalize) { 366 cc.finalize(); 367 } 368 } 369 } 370 } 371 372 template<typename CompilerT> 373 static inline void bench_compiler_func_rt(InitStrategy strategy, size_t count) { 374 JitRuntime rt; 375 CodeHolder code; 376 CompilerT cc; 377 MyErrorHandler eh; 378 379 using Func = uint32_t(*)(void); 380 381 if (strategy == InitStrategy::kInitReset) { 382 for (size_t i = 0; i < count; i++) { 383 code.init(rt.environment()); 384 code.set_error_handler(&eh); 385 code.attach(&cc); 386 387 (void)cc.add_func(FuncSignature::build<uint32_t>()); 388 compile_raw_func(cc); 389 cc.end_func(); 390 cc.finalize(); 391 392 Func fn; 393 rt.add(&fn, &code); 394 rt.release(fn); 395 396 code.reset(); 397 } 398 } 399 else { 400 code.init(rt.environment()); 401 code.set_error_handler(&eh); 402 code.attach(&cc); 403 404 for (size_t i = 0; i < count; i++) { 405 code.reinit(); 406 407 (void)cc.add_func(FuncSignature::build<uint32_t>()); 408 compile_raw_func(cc); 409 cc.end_func(); 410 cc.finalize(); 411 412 Func fn; 413 rt.add(&fn, &code); 414 rt.release(fn); 415 } 416 } 417 } 418 #endif // ASMJIT_HAS_HOST_BACKEND && !ASMJIT_NO_COMPILER 419 420 template<typename Lambda> 421 static inline void test_perf(const char* bench_name, InitStrategy strategy, size_t n, Lambda&& fn) { 422 PerformanceTimer timer; 423 const char* strategy_name = strategy == InitStrategy::kInitReset ? "init/reset" : "reinit "; 424 425 timer.start(); 426 fn(strategy, n); 427 timer.stop(); 428 429 printf("%-31s [%s]: %8.3f [ms]\n", bench_name, strategy_name, timer.duration()); 430 } 431 432 static inline void test_perf_all(InitStrategy strategy, size_t n) { 433 using IS = InitStrategy; 434 435 test_perf("CodeHolder (Only)" , strategy, n, [](IS s, size_t n) { bench_codeholder(s, n); }); 436 437 #if defined(ASMJIT_HAS_HOST_BACKEND) 438 test_perf("Assembler" , strategy, n, [](IS s, size_t n) { bench_assembler<host::Assembler>(s, n); }); 439 test_perf("Assembler + Func" , strategy, n, [](IS s, size_t n) { bench_assembler_func<host::Assembler>(s, n); }); 440 test_perf("Assembler + Func + RT" , strategy, n, [](IS s, size_t n) { bench_assembler_func_rt<host::Assembler>(s, n); }); 441 #endif 442 443 #if defined(ASMJIT_HAS_HOST_BACKEND) && !defined(ASMJIT_NO_BUILDER) 444 test_perf("Builder" , strategy, n, [](IS s, size_t n) { bench_builder<host::Builder>(s, n); }); 445 test_perf("Builder + Func" , strategy, n, [](IS s, size_t n) { bench_builder_func<host::Builder>(s, n, false); }); 446 test_perf("Builder + Func + Finalize" , strategy, n, [](IS s, size_t n) { bench_builder_func<host::Builder>(s, n, true); }); 447 test_perf("Builder + Func + Finalize + RT" , strategy, n, [](IS s, size_t n) { bench_builder_func_finalize_rt<host::Builder>(s, n); }); 448 #endif 449 450 #if defined(ASMJIT_HAS_HOST_BACKEND) && !defined(ASMJIT_NO_COMPILER) 451 452 test_perf("Compiler" , strategy, n, [](IS s, size_t n) { bench_compiler<host::Compiler>(s, n); }); 453 test_perf("Compiler + Func" , strategy, n, [](IS s, size_t n) { bench_compiler_func<host::Compiler>(s, n, false); }); 454 test_perf("Compiler + Func + Finalize" , strategy, n, [](IS s, size_t n) { bench_compiler_func<host::Compiler>(s, n, true); }); 455 456 test_perf("Compiler + Func + Finalize + RT", strategy, n, [](IS s, size_t n) { bench_compiler_func_rt<host::Compiler>(s, n); }); 457 #endif 458 } 459 460 int main(int argc, char* argv[]) { 461 CmdLine cmd_line(argc, argv); 462 size_t n = cmd_line.value_as_uint("--count", 1000000); 463 464 print_app_info(n); 465 466 test_perf_all(InitStrategy::kInitReset, n); 467 printf("\n"); 468 test_perf_all(InitStrategy::kReinit, n); 469 470 return 0; 471 } 472 473 #else 474 475 int main() { 476 print_app_info(0); 477 printf("!!AsmJit Benchmark Reuse is currently disabled: <ASMJIT_NO_JIT> or unsuitable target architecture !!\n"); 478 return 0; 479 } 480 481 #endif