Tiramisu、その1の続き
チュートリアルもあります。
tutorial 01: A simple example of how to use Tiramisu (a simple assignment)
tutorial 02: blurxy
tutorial 03: matrix multiplication
tutorial 05: simple sequence of computations
tutorial 06: reduction example
tutorial 08: update example
tutorial 09: complicate reduction/update example
tutorial 02: blurxy
tutorial 03: matrix multiplication
tutorial 05: simple sequence of computations
tutorial 06: reduction example
tutorial 08: update example
tutorial 09: complicate reduction/update example
int main(int argc, char **argv) { // Set default tiramisu options. global::set_default_tiramisu_options(); global::set_loop_iterator_type(p_int32);
最初に、Tiramisuのオプションにデフォルト値を設定しています。
set_default_tiramisu_optionsは、下記のようになっています。
static void set_default_tiramisu_options() { global::loop_iterator_type = p_int32; set_auto_data_mapping(true); auto location = std::getenv(NVCC_BIN_DIR_ENV_VAR); if (location) nvcc_bin_dir = location; }
set_loop_iterator_typeでは、下記のようにglobal::loop_iterator_typeに設定しているので、
main関数内の global::set_loop_iterator_type(p_int32);は、必要ないね。
main関数内の global::set_loop_iterator_type(p_int32);は、必要ないね。
/** * If this option is set to true, Tiramisu automatically * modifies the computation data mapping whenever a new * schedule is applied to a computation. * If it is set to false, it is up to the user to set * the right data mapping before code generation. */ static void set_auto_data_mapping(bool v) { global::auto_data_mapping = v; }
// Declare a function called "function0". // A function in tiramisu is the equivalent of a function in C. // It can have input and output arguments. These arguments are // represented as buffers and are declared later in the tutorial. function function0("function0");
function の定義ができたら、次は、Layer I の設定です。
Layer I は、Abstract Algorith です。下記は、3と4の加算結果を e3 に代入しています。
Layer I は、Abstract Algorith です。下記は、3と4の加算結果を e3 に代入しています。
// Declare an expression that will be associated to the // computations. This expression sums 3 and 4. expr e3 = expr(3) + expr(4);
次に、function (function0) 内での、computation を宣言します。
computation S0は、各要素の型が uint8 で、サイズが10 x 10。
その各要素には、e3、つまり、7 を代入します。
computation S0は、各要素の型が uint8 で、サイズが10 x 10。
その各要素には、e3、つまり、7 を代入します。
// Declare a computation within function0. // To declare a computation, you need to provide: // (1) an ISL set representing the iteration space of the computation. // Tiramisu uses the ISL syntax to represent sets and maps. The ISL syntax // is described in http://barvinok.gforge.inria.fr/barvinok.pdf (Section // 1.2.1 for sets and iteration domains, and 1.2.2 for maps and access // relations), // (2) a tiramisu expression: this is the expression that will be computed // by the computation. // (3) the function in which the computation will be declared. computation S0("[N]->{S0[i,j]: 0<=i<10 and 0<=j<10}", e3, true, p_uint8, &function0);
// Dump the iteration domain of the function. function0.dump_iteration_domain();
computation のスケジュール
computation を 2x2 のタイル状に分割して、横方向に並列処理する。
スケジュールの結果をダンプする。
computation を 2x2 のタイル状に分割して、横方向に並列処理する。
スケジュールの結果をダンプする。
// Set the schedule of each computation. // The identity schedule means that the program order is not modified // (i.e. no optimization is applied). S0.tile(var("i"), var("j"), 2, 2, var("i0"), var("j0"), var("i1"), var("j1")); S0.tag_parallel_level(var("i0")); // Dump the schedule. function0.dump_schedule();
Layer IIIでは、function0 に割り当てるバッファ(buf0)、各要素の型が uint8で、サイズが10x10を宣言。
// Create a buffer buf0. This buffer is supposed to be allocated outside // the function "function0" and passed to it as an argument. // (actually any buffer of type a_output or a_input should be allocated // by the caller, in contrast to buffers of type a_temporary which are // allocated automatically by the Tiramisu runtime within the callee // and should not be passed as arguments to the function). buffer buf0("buf0", {tiramisu::expr(10), tiramisu::expr(10)}, p_uint8, a_output, &function0);
computation (S0)の出力に、バッファを設定 する
// Map the computations to a buffer (i.e. where each computation // should be stored in the buffer). // This mapping will be updated automatically when the schedule // is applied. To disable automatic data mapping updates use // global::set_auto_data_mapping(false). S0.set_access("{S0[i,j]->buf0[i,j]}");
ここからは、コード生成です。function への引数(出力)として、buf0 を設定します。
// Add buf0 as an argument to the function. function0.set_arguments({&buf0});
// Generate the time-processor domain of the computation // and dump it on stdout. // This is purely for debugging, the tim-processor representation // is not used later on. function0.gen_time_space_domain(); function0.dump_time_processor_domain();
AST(abstract Syntax Tree)を生成します。
その後、function の Halide statement を生成します。
function0.dump_halide_stmt() はデバッグのために、生成した Halide statement をダンプします。
その後、function の Halide statement を生成します。
function0.dump_halide_stmt() はデバッグのために、生成した Halide statement をダンプします。
// Generate an AST (abstract Syntax Tree) function0.gen_isl_ast(); // Generate Halide statement for the function. function0.gen_halide_stmt(); // Dump the Halide stmt generated by gen_halide_stmt() // for the function. function0.dump_halide_stmt();
最後に、function0 のオブジェクトファイル (build/generated_fct_tutorial_01.o) を生成します。
// Generate an object file from the function. function0.gen_halide_obj("build/generated_fct_tutorial_01.o"); return 0; }
生成したオブジェクトファイル (build/generated_fct_tutorial_01.o)を呼び出して使うコードを別途用意します。
#define NN 10 #define MM 10 int main(int, char **) { Halide::Buffer<int32_t> output(NN, MM); init_buffer(output, (int32_t)9); std::cout << "Array (after initialization)" << std::endl; print_buffer(output); function0(output.raw_buffer()); std::cout << "Array after the Halide pipeline" << std::endl; print_buffer(output); Halide::Buffer<int32_t> expected(NN, MM); init_buffer(expected, (int32_t)7); compare_buffers("tutorial_01", output, expected); return 0; }
バッファ (output) を9で初期設定し、function0 の引数に output.raw_buffer() を設定しています。
バッファ (expected) を 7 で初期設定し、compare_buffers にて、output と expected を比較しています。
デバッグ部分は削除して、まとめて書くと
int main(int argc, char **argv) { // Set default tiramisu options. global::set_default_tiramisu_options(); global::set_loop_iterator_type(p_int32); function function0("function0"); // Layer I expr e3 = expr(3) + expr(4); // Layer II computation S0("[N]->{S0[i,j]: 0<=i<10 and 0<=j<10}", e3, true, p_uint8, &function0); S0.tile(var("i"), var("j"), 2, 2, var("i0"), var("j0"), var("i1"), var("j1")); S0.tag_parallel_level(var("i0")); // Layer III buffer buf0("buf0", {tiramisu::expr(10), tiramisu::expr(10)}, p_uint8, a_output, &function0); S0.set_access("{S0[i,j]->buf0[i,j]}"); // コード生成 function0.set_arguments({&buf0}); function0.gen_isl_ast(); function0.gen_halide_stmt(); function0.gen_halide_obj("build/generated_fct_tutorial_01.o"); return 0; }
基本的には、Halide と同じように、関数をオブジェクトファイルとして生成し、
そのオブジェクトファイルをリンクして使うようですね。
そのオブジェクトファイルをリンクして使うようですね。