Class: Ikra::Translator::CommandTranslator::ProgramBuilder::Launcher

Inherits:
Object
  • Object
show all
Defined in:
lib/translator/program_launcher.rb

Defined Under Namespace

Classes: CommandNotifier, FixedSizeArrayStruct, KernelResultStruct, KernelUnionResultStruct, UnionTypeStruct, UnionTypeValue

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source:, environment_builder:, result_type:, root_command:) ⇒ Launcher

Returns a new instance of Launcher.



91
92
93
94
95
96
# File 'lib/translator/program_launcher.rb', line 91

def initialize(source:, environment_builder:, result_type:, root_command:)
    @source = source
    @environment_builder = environment_builder
    @result_type = result_type
    @root_command = root_command
end

Class Attribute Details

.last_time_allocate_memoryObject

Returns the value of attribute last_time_allocate_memory.



73
74
75
# File 'lib/translator/program_launcher.rb', line 73

def last_time_allocate_memory
  @last_time_allocate_memory
end

.last_time_compilerObject

Returns the value of attribute last_time_compiler.



75
76
77
# File 'lib/translator/program_launcher.rb', line 75

def last_time_compiler
  @last_time_compiler
end

.last_time_free_memoryObject

Returns the value of attribute last_time_free_memory.



71
72
73
# File 'lib/translator/program_launcher.rb', line 71

def last_time_free_memory
  @last_time_free_memory
end

.last_time_kernelObject

Returns the value of attribute last_time_kernel.



70
71
72
# File 'lib/translator/program_launcher.rb', line 70

def last_time_kernel
  @last_time_kernel
end

.last_time_prepare_envObject

Returns the value of attribute last_time_prepare_env.



69
70
71
# File 'lib/translator/program_launcher.rb', line 69

def last_time_prepare_env
  @last_time_prepare_env
end

.last_time_read_result_ffiObject

Returns the value of attribute last_time_read_result_ffi.



76
77
78
# File 'lib/translator/program_launcher.rb', line 76

def last_time_read_result_ffi
  @last_time_read_result_ffi
end

.last_time_setup_cudaObject

Returns the value of attribute last_time_setup_cuda.



68
69
70
# File 'lib/translator/program_launcher.rb', line 68

def last_time_setup_cuda
  @last_time_setup_cuda
end

.last_time_total_externalObject

Returns the value of attribute last_time_total_external.



74
75
76
# File 'lib/translator/program_launcher.rb', line 74

def last_time_total_external
  @last_time_total_external
end

.last_time_transfer_memoryObject

Returns the value of attribute last_time_transfer_memory.



72
73
74
# File 'lib/translator/program_launcher.rb', line 72

def last_time_transfer_memory
  @last_time_transfer_memory
end

Instance Attribute Details

#environment_builderObject (readonly)

Returns the value of attribute environment_builder.



64
65
66
# File 'lib/translator/program_launcher.rb', line 64

def environment_builder
  @environment_builder
end

#result_typeObject (readonly)

Returns the value of attribute result_type.



65
66
67
# File 'lib/translator/program_launcher.rb', line 65

def result_type
  @result_type
end

#root_commandObject (readonly)

Returns the value of attribute root_command.



62
63
64
# File 'lib/translator/program_launcher.rb', line 62

def root_command
  @root_command
end

#sourceObject (readonly)

Returns the value of attribute source.



63
64
65
# File 'lib/translator/program_launcher.rb', line 63

def source
  @source
end

Class Method Details

.reset_timeObject



78
79
80
81
82
83
84
85
86
87
88
# File 'lib/translator/program_launcher.rb', line 78

def reset_time
    @last_time_setup_cuda = 0
    @last_time_prepare_env = 0
    @last_time_kernel = 0
    @last_time_free_memory = 0
    @last_time_transfer_memory = 0
    @last_time_allocate_memory = 0
    @last_time_total_external = 0
    @last_time_compiler = 0
    @last_time_read_result_ffi = 0
end

Instance Method Details

#compileObject



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/translator/program_launcher.rb', line 98

def compile
    # Generate debug output with line numbers
    line_no_digits = Math.log(source.lines.count, 10).ceil
    source_with_line_numbers = source.lines.each_with_index.map do |line, num| 
        "[#{(num + 1).to_s.rjust(line_no_digits, "0")}] #{line}" 
    end.join("")

    Log.info("Generated source code:\n#{source_with_line_numbers}")

    # Write source code to temporary file
    file = Tempfile.new(["ikra_kernel", ".cu"])
    file.write(source)
    file.close

    # Write to codegen_expect
    if Configuration.codegen_expect_file_name != nil
        expect_file = File.new(Configuration.codegen_expect_file_name, "w+")
        expect_file.write(source)
        expect_file.close
    end

    # Run compiler
    @so_filename = "#{file.path}.#{Configuration.so_suffix}"
    nvcc_command = Configuration.nvcc_invocation_string(
        file.path, @so_filename)
    Log.info("Compiling kernel: #{nvcc_command}")
    time_before = Time.now
    compile_status = %x(#{nvcc_command})
    Log.info("Done, took #{Time.now - time_before} s")
    self.class.last_time_compiler = Time.now - time_before

    if $? != 0
        Log.fatal("nvcc failed: #{compile_status}")
        raise RuntimeError.new("nvcc failed: #{compile_status}")
    else
        Log.info("nvcc successful: #{compile_status}")
    end
end

#executeObject

Attaches the compiled shared library via Ruby FFI and invokes the kernel.



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/translator/program_launcher.rb', line 138

def execute
    if !File.exist?(@so_filename)
        compile
    end

    time_before = Time.now
    ffi_interface = Module.new
    ffi_interface.extend(FFI::Library)
    ffi_interface.ffi_lib(@so_filename)
    ffi_interface.attach_function(:launch_kernel, [:pointer], :pointer)
    environment_object = environment_builder.build_ffi_object
    Log.info("FFI transfer time: #{Time.now - time_before} s")

    time_before = Time.now
    kernel_result = ffi_interface.launch_kernel(environment_object)
    total_time_external = Time.now - time_before
    Log.info("Kernel time: #{total_time_external} s")

    # Update command
    root_command.accept(CommandNotifier.new(environment_builder.ffi_struct))

    # TODO: Currently, this only works if result_type.is_singleton?
    if result_type.is_singleton?
        result_t_struct = KernelResultStruct.new(kernel_result)
    else
        result_t_struct = KernelUnionResultStruct.new(kernel_result)
    end

    # Extract error code and return value
    error_code = result_t_struct[:error_code]

    # Extract time measurements
    self.class.last_time_setup_cuda += result_t_struct[:time_setup_cuda] * 0.000001
    self.class.last_time_prepare_env += result_t_struct[:time_prepare_env] * 0.000001
    self.class.last_time_kernel += result_t_struct[:time_kernel] * 0.000001
    self.class.last_time_free_memory += result_t_struct[:time_free_memory] * 0.000001
    self.class.last_time_transfer_memory += result_t_struct[:time_transfer_memory] * 0.000001
    self.class.last_time_allocate_memory += result_t_struct[:time_allocate_memory] * 0.000001
    self.class.last_time_total_external += total_time_external

    if error_code != 0
        # Kernel failed
        Errors.raiseCudaError(error_code)
    end

    time_before = Time.now

    # Check type of result: It should be one of `result_type`
    if result_type.is_singleton?
       array_type = result_type.singleton_type

        if !array_type.is_a?(Types::ArrayType)
            raise AssertionError.new(
                "ArrayType expected, but #{array_type} found")
        end

        result = result_t_struct[:result][:content]
        result_size = result_t_struct[:result][:size]
    else
        array_type = result_type.find do |sing_type|
            sing_type.class_id == result_t_struct[:result][:class_id]
        end

        if array_type == nil
            raise AssertionError.new(
                "Unknown class_id: #{result_t_struct[:result][:class_id]}")
        end

        if !array_type.is_a?(Types::ArrayType)
            raise AssertionError.new(
                "ArrayType expected, but #{array_type} found")
        end

        result = result_t_struct[:result][:value][:variable_size_array][:content]
        result_size = result_t_struct[:result][:value][:variable_size_array][:size]
    end

    inner_type = array_type.inner_type

    if inner_type.is_singleton?
        # Read in entire array
        if inner_type.singleton_type == Types::PrimitiveType::Int
            computation_result = result.read_array_of_int(result_size)
        elsif inner_type.singleton_type == Types::PrimitiveType::Float
            computation_result = result.read_array_of_float(result_size)
        elsif inner_type.singleton_type == Types::PrimitiveType::Bool
            computation_result = result.read_array_of_uint8(result_size).map do |v|
                v == 1
            end
        elsif inner_type.singleton_type == Types::PrimitiveType::Nil
            computation_result = [nil] * result_size
        elsif inner_type.singleton_type.is_a?(Types::ZipStructType)
            result_struct_type = inner_type.singleton_type.to_ruby_type
            computation_result = Array.new(result_size) do |index|
                result_struct_type.new(result + index * result_struct_type.size)
            end
        else
            raise NotImplementedError.new("Type not implemented")
        end

        self.class.last_time_read_result_ffi = Time.now - time_before
        return computation_result
    else
        # Read union type struct
        # Have to read one by one and assemble object
        result_values = Array.new(result_size)

        for index in 0...result_size
            # TODO: Size of union type (12 bytes) should not be hard-coded here
            s = Constants::UNION_TYPE_SIZE
            o = Constants::UNION_TYPE_VALUE_OFFSET
            next_type = (result + (s * index)).read_int

            if next_type == Types::PrimitiveType::Int.class_id
                result_values[index] = (result + s * index + o).read_int
            elsif next_type == Types::PrimitiveType::Float.class_id
                result_values[index] = (result + s * index + o).read_float
            elsif next_type == Types::PrimitiveType::Bool.class_id
                result_values[index] = (result + s * index + o).read_uint8 == 1
            elsif next_type == Types::PrimitiveType::Nil.class_id
                result_values[index] = nil
            else
                raise NotImplementedError.new("Implement class objs for \##{index}: #{next_type}")
            end
        end

        self.class.last_time_read_result_ffi = Time.now - time_before
        return result_values
    end
end