class Ikra::Translator::CommandTranslator::ProgramBuilder
Builds the entire CUDA program. A CUDA program may consist of multiple kernels, but has at least one kernel. The generated code performs the following steps:
-
Insert header of CUDA file.
-
For every kernel: Build all methods, blocks, and kernels.
-
Build the program entry point (including kernel launchers).
Attributes
An array of array command structs.
An array of structs definitions ([Types::StructType] instances) that should be generated for this program.
Public Class Methods
# File lib/translator/program_builder.rb, line 27 def initialize(environment_builder:, root_command:) @kernel_launchers = [] @kernels = Set.new([]) @environment_builder = environment_builder @root_command = root_command # The collection of structs is a [Set]. Struct types are unique, i.e., there # are never two equal struct types with different object identity. @structs = Set.new @array_command_structs = Set.new end
Public Instance Methods
# File lib/translator/program_builder.rb, line 39 def add_array_command_struct(*structs) for struct in structs array_command_structs.add(struct) end end
# File lib/translator/program_builder.rb, line 45 def add_kernel_launcher(launcher) @kernel_launchers.push(launcher) end
Build kernel invocations
# File lib/translator/program_builder.rb, line 65 def build_kernel_launchers return kernel_launchers.map do |launcher| launcher.build_kernel_launcher end.join("") end
Generates the source code for the CUDA program, compiles it with nvcc and executes the program.
# File lib/translator/program_builder.rb, line 51 def execute source = build_program launcher = Launcher.new( source: source, environment_builder: environment_builder, result_type: result_type, root_command: root_command) launcher.compile return launcher.execute end
Protected Instance Methods
# File lib/translator/program_builder.rb, line 101 def all_kernel_builders return kernel_launchers.map do |launcher| launcher.kernel_builders end.flatten end
# File lib/translator/program_builder.rb, line 73 def assert_ready_to_build if kernel_launchers.size == 0 raise AssertionError.new( "Not ready to build (ProgramBuilder): No kernel launcher defined") end end
# File lib/translator/program_builder.rb, line 97 def build_array_command_struct_types return array_command_structs.to_a.join("\n") + "\n" end
Build environment struct definition
# File lib/translator/program_builder.rb, line 86 def build_environment_struct return environment_builder.build_environment_struct end
Build header of CUDA source code file
# File lib/translator/program_builder.rb, line 81 def build_header return Translator.read_file(file_name: "header.cpp") end
Build the struct type for `result_t`.
# File lib/translator/program_builder.rb, line 171 def build_header_structs header_structs = Translator.read_file(file_name: "header_structs.cpp", replacements: {"result_type" => result_type.to_c_type}) end
Build methods, blocks and kernels
# File lib/translator/program_builder.rb, line 108 def build_kernels result = "" for builder in all_kernel_builders # Check whether kernel was already build before if kernels.include?(builder) next else kernels.add(builder) end result = result + builder.build_methods result = result + builder.build_blocks result = result + builder.build_kernel end return result end
Free device memory
# File lib/translator/program_builder.rb, line 158 def build_memory_free result = "" for launcher in kernel_launchers if !launcher.reuse_memory? result = result + launcher.build_device_memory_free end end return result end
Builds the CUDA program. Returns the source code string.
# File lib/translator/program_builder.rb, line 177 def build_program assert_ready_to_build result = build_header + build_struct_types + build_header_structs + build_array_command_struct_types + build_environment_struct + build_kernels # Build program entry point return result + Translator.read_file(file_name: "entry_point.cpp", replacements: { "prepare_environment" => environment_builder.build_environment_variable, "launch_all_kernels" => build_kernel_launchers, "free_device_memory" => build_memory_free, "host_env_var_name" => Constants::ENV_HOST_IDENTIFIER, "host_result_array" => host_result_expression}) end
Generate all struct types (except for array command struct types).
# File lib/translator/program_builder.rb, line 91 def build_struct_types return structs.map do |struct_type| struct_type.generate_definition end.join("\n") + "\n" end
# File lib/translator/program_builder.rb, line 127 def host_result_expression # Read some fields from last kernel launch configuration result_device_ptr = kernel_launchers.last.kernel_result_var_name result_c_type = kernel_launchers.last.result_type.to_c_type result_size = root_command.size if result_device_ptr == nil raise AssertionError.new( "Result variable name of final kernel launcher not set") end # Build result values: `variable_size_array_t` struct. This struct contains a # pointer to the result array and stores the size of the result. result_device_variable_array_t = "variable_size_array_t((void *) #{result_device_ptr}, #{result_size})" return Translator.read_file(file_name: "memcpy_device_to_host_expr.cpp", replacements: { "type" => result_c_type, "device_array" => result_device_variable_array_t}) end
Returns the result type of this program. The result type must always be a union type that includes a [Types::LocationAwareArrayType] object, because this way we can support return types where the inner type of an array is unknown at compile time.
# File lib/translator/program_builder.rb, line 151 def result_type return Types::LocationAwareVariableSizeArrayType.new( kernel_launchers.last.result_type, location: :host).to_union_type end