1 """Common utilities that are useful for all the benchmarks."""
5 from mlir
.dialects
import arith
6 from mlir
.dialects
import func
7 from mlir
.dialects
import memref
8 from mlir
.dialects
import scf
9 from mlir
.passmanager
import PassManager
12 def setup_passes(mlir_module
):
13 """Setup pass pipeline parameters for benchmark functions."""
15 "parallelization-strategy=none"
17 pipeline
= f
"builtin.module(sparsifier{{{opt}}})"
18 PassManager
.parse(pipeline
).run(mlir_module
.operation
)
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`
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
)
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)
39 def get_kernel_func_from_module(module
: ir
.Module
) -> func
.FuncOp
:
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
45 len(module
.operation
.regions
) == 1
46 ), "Expected kernel module to have only one region"
48 len(module
.operation
.regions
[0].blocks
) == 1
49 ), "Expected kernel module to have only one block"
51 len(module
.operation
.regions
[0].blocks
[0].operations
) == 1
52 ), "Expected kernel module to have only one operation"
53 return module
.operation
.regions
[0].blocks
[0].operations
[0]
56 def emit_timer_func() -> func
.FuncOp
:
57 """Returns the declaration of nanoTime function. If nanoTime function is
58 used, the `MLIR_RUNNER_UTILS` and `MLIR_C_RUNNER_UTILS` must be included.
60 i64_type
= ir
.IntegerType
.get_signless(64)
61 nanoTime
= func
.FuncOp("nanoTime", ([], [i64_type
]), visibility
="private")
62 nanoTime
.attributes
["llvm.emit_c_interface"] = ir
.UnitAttr
.get()
66 def emit_benchmark_wrapped_main_func(kernel_func
, timer_func
):
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
71 len(kernel_func.type.results) times. This function can be used to
72 create a "time measuring" variant of a function.
74 i64_type
= ir
.IntegerType
.get_signless(64)
75 memref_of_i64_type
= ir
.MemRefType
.get([ir
.ShapedType
.get_dynamic_size()], i64_type
)
76 wrapped_func
= func
.FuncOp(
77 # Same signature and an extra buffer of indices to save timings.
79 (kernel_func
.arguments
.types
+ [memref_of_i64_type
], kernel_func
.type.results
),
82 wrapped_func
.attributes
["llvm.emit_c_interface"] = ir
.UnitAttr
.get()
84 num_results
= len(kernel_func
.type.results
)
85 with ir
.InsertionPoint(wrapped_func
.add_entry_block()):
86 timer_buffer
= wrapped_func
.arguments
[-1]
87 zero
= arith
.ConstantOp
.create_index(0)
88 n_iterations
= memref
.DimOp(timer_buffer
, zero
)
89 one
= arith
.ConstantOp
.create_index(1)
90 iter_args
= list(wrapped_func
.arguments
[-num_results
- 1 : -1])
91 loop
= scf
.ForOp(zero
, n_iterations
, one
, iter_args
)
92 with ir
.InsertionPoint(loop
.body
):
93 start
= func
.CallOp(timer_func
, [])
96 wrapped_func
.arguments
[: -num_results
- 1] + loop
.inner_iter_args
,
98 end
= func
.CallOp(timer_func
, [])
99 time_taken
= arith
.SubIOp(end
, start
)
100 memref
.StoreOp(time_taken
, timer_buffer
, [loop
.induction_variable
])
101 scf
.YieldOp(list(call
.results
))