blob: 0b924172c8dad7430385ddc3d87522f962133ad1 [file] [log] [blame]
Saurabh Jhafa90c9d2022-01-27 21:35:341"""Common utilities that are useful for all the benchmarks."""
2import numpy as np
3
Saurabh Jhafa90c9d2022-01-27 21:35:344from mlir import ir
5from mlir.dialects import arith
River Riddle23aa5a72022-02-26 22:49:546from mlir.dialects import func
Saurabh Jhafa90c9d2022-01-27 21:35:347from mlir.dialects import memref
8from mlir.dialects import scf
Saurabh Jhafa90c9d2022-01-27 21:35:349from mlir.passmanager import PassManager
10
11
12def setup_passes(mlir_module):
Tobias Hietaf9008e62023-05-17 14:53:3913 """Setup pass pipeline parameters for benchmark functions."""
Saurabh Jhafa90c9d2022-01-27 21:35:3414 opt = (
Nick Kreeger30ceb782022-09-04 01:39:3515 "parallelization-strategy=none"
Saurabh Jhafa90c9d2022-01-27 21:35:3416 )
Kohei Yamaguchia05e20b2023-12-06 06:47:5317 pipeline = f"builtin.module(sparsifier{{{opt}}})"
18 PassManager.parse(pipeline).run(mlir_module.operation)
Saurabh Jhafa90c9d2022-01-27 21:35:3419
20
21def 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 Riddle36550692022-03-08 03:16:0339def get_kernel_func_from_module(module: ir.Module) -> func.FuncOp:
Saurabh Jhafa90c9d2022-01-27 21:35:3440 """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 Hietaf9008e62023-05-17 14:53:3944 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 Jhafa90c9d2022-01-27 21:35:3453 return module.operation.regions[0].blocks[0].operations[0]
54
55
River Riddle36550692022-03-08 03:16:0356def emit_timer_func() -> func.FuncOp:
Denys Shabalin89d49042022-05-09 10:46:3657 """Returns the declaration of nanoTime function. If nanoTime function is
Saurabh Jhafa90c9d2022-01-27 21:35:3458 used, the `MLIR_RUNNER_UTILS` and `MLIR_C_RUNNER_UTILS` must be included.
59 """
60 i64_type = ir.IntegerType.get_signless(64)
Tobias Hietaf9008e62023-05-17 14:53:3961 nanoTime = func.FuncOp("nanoTime", ([], [i64_type]), visibility="private")
Denys Shabalin89d49042022-05-09 10:46:3662 nanoTime.attributes["llvm.emit_c_interface"] = ir.UnitAttr.get()
63 return nanoTime
Saurabh Jhafa90c9d2022-01-27 21:35:3464
65
Ingo Müller5da54832022-07-14 08:04:2166def emit_benchmark_wrapped_main_func(kernel_func, timer_func):
Saurabh Jhafa90c9d2022-01-27 21:35:3467 """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üller5da54832022-07-14 08:04:2171 len(kernel_func.type.results) times. This function can be used to
72 create a "time measuring" variant of a function.
Saurabh Jhafa90c9d2022-01-27 21:35:3473 """
74 i64_type = ir.IntegerType.get_signless(64)
Kohei Yamaguchia05e20b2023-12-06 06:47:5375 memref_of_i64_type = ir.MemRefType.get([ir.ShapedType.get_dynamic_size()], i64_type)
River Riddle36550692022-03-08 03:16:0376 wrapped_func = func.FuncOp(
Saurabh Jhafa90c9d2022-01-27 21:35:3477 # Same signature and an extra buffer of indices to save timings.
78 "main",
Tobias Hietaf9008e62023-05-17 14:53:3979 (kernel_func.arguments.types + [memref_of_i64_type], kernel_func.type.results),
80 visibility="public",
Saurabh Jhafa90c9d2022-01-27 21:35:3481 )
82 wrapped_func.attributes["llvm.emit_c_interface"] = ir.UnitAttr.get()
83
Ingo Müller5da54832022-07-14 08:04:2184 num_results = len(kernel_func.type.results)
Saurabh Jhafa90c9d2022-01-27 21:35:3485 with ir.InsertionPoint(wrapped_func.add_entry_block()):
86 timer_buffer = wrapped_func.arguments[-1]
87 zero = arith.ConstantOp.create_index(0)
Kohei Yamaguchia05e20b2023-12-06 06:47:5388 n_iterations = memref.DimOp(timer_buffer, zero)
Saurabh Jhafa90c9d2022-01-27 21:35:3489 one = arith.ConstantOp.create_index(1)
Tobias Hietaf9008e62023-05-17 14:53:3990 iter_args = list(wrapped_func.arguments[-num_results - 1 : -1])
Saurabh Jhafa90c9d2022-01-27 21:35:3491 loop = scf.ForOp(zero, n_iterations, one, iter_args)
92 with ir.InsertionPoint(loop.body):
River Riddle23aa5a72022-02-26 22:49:5493 start = func.CallOp(timer_func, [])
94 call = func.CallOp(
Ingo Müller5da54832022-07-14 08:04:2195 kernel_func,
Tobias Hietaf9008e62023-05-17 14:53:3996 wrapped_func.arguments[: -num_results - 1] + loop.inner_iter_args,
Saurabh Jhafa90c9d2022-01-27 21:35:3497 )
River Riddle23aa5a72022-02-26 22:49:5498 end = func.CallOp(timer_func, [])
Saurabh Jhafa90c9d2022-01-27 21:35:3499 time_taken = arith.SubIOp(end, start)
100 memref.StoreOp(time_taken, timer_buffer, [loop.induction_variable])
101 scf.YieldOp(list(call.results))
River Riddle23aa5a72022-02-26 22:49:54102 func.ReturnOp(loop)
Saurabh Jhafa90c9d2022-01-27 21:35:34103
104 return wrapped_func