Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 1 | """Common utilities that are useful for all the benchmarks.""" |
| 2 | import numpy as np |
| 3 | |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 4 | from mlir import ir |
| 5 | from mlir.dialects import arith |
River Riddle | 23aa5a7 | 2022-02-26 22:49:54 | [diff] [blame] | 6 | from mlir.dialects import func |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 7 | from mlir.dialects import memref |
| 8 | from mlir.dialects import scf |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 9 | from mlir.passmanager import PassManager |
| 10 | |
| 11 | |
| 12 | def setup_passes(mlir_module): |
Tobias Hieta | f9008e6 | 2023-05-17 14:53:39 | [diff] [blame] | 13 | """Setup pass pipeline parameters for benchmark functions.""" |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 14 | opt = ( |
Nick Kreeger | 30ceb78 | 2022-09-04 01:39:35 | [diff] [blame] | 15 | "parallelization-strategy=none" |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 16 | ) |
Kohei Yamaguchi | a05e20b | 2023-12-06 06:47:53 | [diff] [blame] | 17 | pipeline = f"builtin.module(sparsifier{{{opt}}})" |
| 18 | PassManager.parse(pipeline).run(mlir_module.operation) |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 19 | |
| 20 | |
| 21 | def create_sparse_np_tensor(dimensions, number_of_elements): |
| 22 | """Constructs a numpy tensor of dimensions `dimensions` that has only a |
| 23 | specific number of nonzero elements, specified by the `number_of_elements` |
| 24 | argument. |
| 25 | """ |
| 26 | tensor = np.zeros(dimensions, np.float64) |
| 27 | tensor_indices_list = [ |
| 28 | [np.random.randint(0, dimension) for dimension in dimensions] |
| 29 | for _ in range(number_of_elements) |
| 30 | ] |
| 31 | for tensor_indices in tensor_indices_list: |
| 32 | current_tensor = tensor |
| 33 | for tensor_index in tensor_indices[:-1]: |
| 34 | current_tensor = current_tensor[tensor_index] |
| 35 | current_tensor[tensor_indices[-1]] = np.random.uniform(1, 100) |
| 36 | return tensor |
| 37 | |
| 38 | |
River Riddle | 3655069 | 2022-03-08 03:16:03 | [diff] [blame] | 39 | def get_kernel_func_from_module(module: ir.Module) -> func.FuncOp: |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 40 | """Takes an mlir module object and extracts the function object out of it. |
| 41 | This function only works for a module with one region, one block, and one |
| 42 | operation. |
| 43 | """ |
Tobias Hieta | f9008e6 | 2023-05-17 14:53:39 | [diff] [blame] | 44 | assert ( |
| 45 | len(module.operation.regions) == 1 |
| 46 | ), "Expected kernel module to have only one region" |
| 47 | assert ( |
| 48 | len(module.operation.regions[0].blocks) == 1 |
| 49 | ), "Expected kernel module to have only one block" |
| 50 | assert ( |
| 51 | len(module.operation.regions[0].blocks[0].operations) == 1 |
| 52 | ), "Expected kernel module to have only one operation" |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 53 | return module.operation.regions[0].blocks[0].operations[0] |
| 54 | |
| 55 | |
River Riddle | 3655069 | 2022-03-08 03:16:03 | [diff] [blame] | 56 | def emit_timer_func() -> func.FuncOp: |
Denys Shabalin | 89d4904 | 2022-05-09 10:46:36 | [diff] [blame] | 57 | """Returns the declaration of nanoTime function. If nanoTime function is |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 58 | used, the `MLIR_RUNNER_UTILS` and `MLIR_C_RUNNER_UTILS` must be included. |
| 59 | """ |
| 60 | i64_type = ir.IntegerType.get_signless(64) |
Tobias Hieta | f9008e6 | 2023-05-17 14:53:39 | [diff] [blame] | 61 | nanoTime = func.FuncOp("nanoTime", ([], [i64_type]), visibility="private") |
Denys Shabalin | 89d4904 | 2022-05-09 10:46:36 | [diff] [blame] | 62 | nanoTime.attributes["llvm.emit_c_interface"] = ir.UnitAttr.get() |
| 63 | return nanoTime |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 64 | |
| 65 | |
Ingo Müller | 5da5483 | 2022-07-14 08:04:21 | [diff] [blame] | 66 | def emit_benchmark_wrapped_main_func(kernel_func, timer_func): |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 67 | """Takes a function and a timer function, both represented as FuncOp |
| 68 | objects, and returns a new function. This new function wraps the call to |
| 69 | the original function between calls to the timer_func and this wrapping |
| 70 | in turn is executed inside a loop. The loop is executed |
Ingo Müller | 5da5483 | 2022-07-14 08:04:21 | [diff] [blame] | 71 | len(kernel_func.type.results) times. This function can be used to |
| 72 | create a "time measuring" variant of a function. |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 73 | """ |
| 74 | i64_type = ir.IntegerType.get_signless(64) |
Kohei Yamaguchi | a05e20b | 2023-12-06 06:47:53 | [diff] [blame] | 75 | memref_of_i64_type = ir.MemRefType.get([ir.ShapedType.get_dynamic_size()], i64_type) |
River Riddle | 3655069 | 2022-03-08 03:16:03 | [diff] [blame] | 76 | wrapped_func = func.FuncOp( |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 77 | # Same signature and an extra buffer of indices to save timings. |
| 78 | "main", |
Tobias Hieta | f9008e6 | 2023-05-17 14:53:39 | [diff] [blame] | 79 | (kernel_func.arguments.types + [memref_of_i64_type], kernel_func.type.results), |
| 80 | visibility="public", |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 81 | ) |
| 82 | wrapped_func.attributes["llvm.emit_c_interface"] = ir.UnitAttr.get() |
| 83 | |
Ingo Müller | 5da5483 | 2022-07-14 08:04:21 | [diff] [blame] | 84 | num_results = len(kernel_func.type.results) |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 85 | with ir.InsertionPoint(wrapped_func.add_entry_block()): |
| 86 | timer_buffer = wrapped_func.arguments[-1] |
| 87 | zero = arith.ConstantOp.create_index(0) |
Kohei Yamaguchi | a05e20b | 2023-12-06 06:47:53 | [diff] [blame] | 88 | n_iterations = memref.DimOp(timer_buffer, zero) |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 89 | one = arith.ConstantOp.create_index(1) |
Tobias Hieta | f9008e6 | 2023-05-17 14:53:39 | [diff] [blame] | 90 | iter_args = list(wrapped_func.arguments[-num_results - 1 : -1]) |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 91 | loop = scf.ForOp(zero, n_iterations, one, iter_args) |
| 92 | with ir.InsertionPoint(loop.body): |
River Riddle | 23aa5a7 | 2022-02-26 22:49:54 | [diff] [blame] | 93 | start = func.CallOp(timer_func, []) |
| 94 | call = func.CallOp( |
Ingo Müller | 5da5483 | 2022-07-14 08:04:21 | [diff] [blame] | 95 | kernel_func, |
Tobias Hieta | f9008e6 | 2023-05-17 14:53:39 | [diff] [blame] | 96 | wrapped_func.arguments[: -num_results - 1] + loop.inner_iter_args, |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 97 | ) |
River Riddle | 23aa5a7 | 2022-02-26 22:49:54 | [diff] [blame] | 98 | end = func.CallOp(timer_func, []) |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 99 | time_taken = arith.SubIOp(end, start) |
| 100 | memref.StoreOp(time_taken, timer_buffer, [loop.induction_variable]) |
| 101 | scf.YieldOp(list(call.results)) |
River Riddle | 23aa5a7 | 2022-02-26 22:49:54 | [diff] [blame] | 102 | func.ReturnOp(loop) |
Saurabh Jha | fa90c9d | 2022-01-27 21:35:34 | [diff] [blame] | 103 | |
| 104 | return wrapped_func |