Class: Bones::Algorithm

Inherits:
Common
  • Object
show all
Defined in:
lib/bones/algorithm.rb

Overview

This class holds one algorithm, which includes a species, a name, and the source C-code.

The algorithm class holds all sorts of information on var- iables. This information is only available after calling the ‘populate’ method, which populates a lists of varia- bles of all sorts: a regular list, a specialized hash, and lists of input/output array variables.

Constant Summary collapse

ACCELERATED =

Constant to set the name of the algorithm’s accelerated version

'_accelerated'
ORIGINAL =

Constant to set the name of the algorithm’s original version

'_original'

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Common

#flatten_hash, #from, #replace_defines, #search_and_replace, #search_and_replace!, #sum, #sum_and_from, #to

Constructor Details

#initialize(name, filename, id, species, code) ⇒ Algorithm

This method initializes the class. It gives the new algorithm a name, species and source code. At initiali- zation, this method checks if the name starts with a digit. This is not allowed, so an underscore is added prior to the digit.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/bones/algorithm.rb', line 25

def initialize(name, filename, id, species, code)
  name = '_'+name if name =~ /^\d/
  @filename = filename
  @basename = name
  @name = (name+'_'+id).gsub(/\W/,'')
  @id = id
  @original_name = @name+ORIGINAL
  @accelerated_name = @name+ACCELERATED
  @species = species
  begin
    @code = C::Statement.parse(code).preprocess
  rescue
    @code = C::Statement.parse('{'+code+'}').preprocess
  end
  @hash = {}
  @lists = {:host_name => [],:host_definition => [], :argument_name => [], :argument_definition => [], :golden_name => []}
  @arrays = Variablelist.new()
  @constants = Variablelist.new()
  @merge_factor = 0
  @register_caching_enabled = 1
  @function_code = ''
  @function_name = ''
  
  # Set the initial hash
  @hash = {:algorithm_id        => @id,
           :algorithm_name      => @name,
           :algorithm_basename  => @basename,
           :algorithm_filename  => @filename}
end

Instance Attribute Details

#arraysObject (readonly)

Returns the value of attribute arrays.



12
13
14
# File 'lib/bones/algorithm.rb', line 12

def arrays
  @arrays
end

#codeObject (readonly)

Returns the value of attribute code.



12
13
14
# File 'lib/bones/algorithm.rb', line 12

def code
  @code
end

#function_nameObject (readonly)

Returns the value of attribute function_name.



12
13
14
# File 'lib/bones/algorithm.rb', line 12

def function_name
  @function_name
end

#hashObject

Returns the value of attribute hash.



13
14
15
# File 'lib/bones/algorithm.rb', line 13

def hash
  @hash
end

#idObject (readonly)

Returns the value of attribute id.



12
13
14
# File 'lib/bones/algorithm.rb', line 12

def id
  @id
end

#listsObject (readonly)

Returns the value of attribute lists.



12
13
14
# File 'lib/bones/algorithm.rb', line 12

def lists
  @lists
end

#merge_factorObject

Returns the value of attribute merge_factor.



13
14
15
# File 'lib/bones/algorithm.rb', line 13

def merge_factor
  @merge_factor
end

#nameObject (readonly)

Returns the value of attribute name.



12
13
14
# File 'lib/bones/algorithm.rb', line 12

def name
  @name
end

#register_caching_enabledObject

Returns the value of attribute register_caching_enabled.



13
14
15
# File 'lib/bones/algorithm.rb', line 13

def register_caching_enabled
  @register_caching_enabled
end

#speciesObject (readonly)

Returns the value of attribute species.



12
13
14
# File 'lib/bones/algorithm.rb', line 12

def species
  @species
end

Instance Method Details

#generate_replacement_code(options, skeleton, verify_code, prefix, timer_start, timer_stop) ⇒ Object

This method is used to generate verification code. This verification code contains a copy of the original code. It also provides a verification which compares the output of the original code with the output of the generated code. The verification code prints warnings if the outputs are not equal, else it prints a success message.



512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
# File 'lib/bones/algorithm.rb', line 512

def generate_replacement_code(options, skeleton, verify_code, prefix, timer_start, timer_stop)
  replacement = C::NodeArray.new
  replacement.push(C::ExpressionStatement.parse(@accelerated_name+'('+@lists[:host_name]+');'))
  original_definition = ''
  verify_definitions = []
  if options[:verify]
    guesses = @arrays.map { |array| array.guess }
    if guesses.include?(true)
      puts WARNING+'Verification not supported for this class'
    else
      
      # Generate the replacement code and the original function
      @arrays.each do |array|
        replacement.insert(0,C::ExpressionStatement.parse("memcpy(#{array.golden_name},#{array.name},#{array.size.join('*')}*sizeof(#{array.type_name}));"))
        replacement.insert(0,C::Declaration.parse(array.definition.gsub!(/\b#{array.name}\b/,array.golden_name)+array.initialization))
      end
      replacement.push(C::ExpressionStatement.parse(@original_name+'('+@lists[:golden_name]+');'))
      original_definition = "void #{@original_name}(#{@lists[:host_definition]})"
      body = "#{timer_start}#{NL}  // Original code#{NL}#{@code}#{NL}#{timer_stop}"
      verify_code.push(prefix+original_definition+' {'+NL+body+'}'+NL+NL)
      @arrays.select(OUTPUT).each do |array|
        replacement.push(C::ExpressionStatement.parse(("bones_verify_results_#{array.name}_#{@id}(#{array.name}#{array.flatten},#{array.golden_name}#{array.flatten},#{@hash[:argument_name]});").remove_extras))
      end
      @arrays.each do |array|
        replacement.push(C::ExpressionStatement.parse("free(#{array.golden_name});")) if array.dynamic?
      end
      
      # Generate the verification function itself
      @arrays.select(OUTPUT).each_with_index do |array,num_array|
        minihash = @hash["out#{num_array}".to_sym]
        minihash[:name] = minihash[:name]+'_'+@id
        minihash[:argument_definition] = @hash[:argument_definition]
        instantiated_skeleton = search_and_replace(minihash,skeleton)
        verify_definitions.push(instantiated_skeleton.scan(/#{START_DEFINITION}(.+)#{END_DEFINITION}/m).join.strip.remove_extras)
        verify_code.push(instantiated_skeleton.remove_extras.gsub!(/#{START_DEFINITION}(.+)#{END_DEFINITION}/m,''))
      end
    end
  end
  return replacement, original_definition, verify_definitions.join(NL)
end

#opencl_arguments(list, kernel_id) ⇒ Object

Helper function to create a the special code which is required for OpenCL function calls to be able to use kernel arguments.



360
361
362
363
364
365
366
367
# File 'lib/bones/algorithm.rb', line 360

def opencl_arguments(list,kernel_id)
  return '' if list == ''
  argument_string = ''
  list.split(', ').each_with_index do |variable,id|
    argument_string += 'clSetKernelArg(bones_kernel_'+@name+'_'+kernel_id.to_s+',bones_num_args+'+id.to_s+',sizeof('+variable.strip+'),(void*)&'+variable.strip+');'+NL+INDENT
  end
  return argument_string
end

#perform_transformations(transformation_settings) ⇒ Object

This method performs the code transformations according to the transformation settings as provided as an argument to the function. It calls the various code transformation functions as implemented for the CAST class. The resulting modified code is finally stored in the search-and-replace hash. This method assumes that the populate method has already been called, such that the hash contains the dimensions needed to create the global ID definitions.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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
136
137
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
# File 'lib/bones/algorithm.rb', line 79

def perform_transformations(transformation_settings)
  complexity = 0
  
  # Save the original code (with flattened arrays) in the hash as well
  new_code = @code.clone
  @arrays.each do |array|
    new_code.transform_flatten(array)
  end
  @hash[:algorithm_code0] = new_code.to_s
  
  # Loop over the number of transformation 'blocks'
  transformation_settings.split(' ').each_with_index do |transformation,num_transformation|
    new_code = @code.clone
    extra_indent = ''
    
    # Replace existing loops in the code (always do this)
    array = @arrays.representative
    array.species.dimensions.each_with_index do |dimension,num_dimension|
      index         =  (array.species.reverse?) ? num_dimension : array.species.dimensions.length-num_dimension-1
      index_reverse = !(array.species.reverse?) ? num_dimension : array.species.dimensions.length-num_dimension-1
      
      # Calculate the loop start and end conditions
      from = array.species.from_at(index)
      to = array.species.to_at(index)
      
      # Process the existing code and update the hash
      if from != to
        new_code, loop_variable_name = new_code.remove_loop(from,to)
        new_variable_name = GLOBAL_ID+'_'+index_reverse.to_s
        new_code.replace_variable(loop_variable_name,new_variable_name)
        update_hash(loop_variable_name)
      end
    end
    
    # Shuffle the indices of the first input(s) (conditionally do this)
    shuffle_arrays = []
    if transformation[0,1] == '2'
      shuffle_arrays.push(@arrays.select(INPUT)[0])
    elsif transformation[0,1] == '3'
      shuffle_arrays.push(@arrays.select(INPUT)[0])
      shuffle_arrays.push(@arrays.select(INPUT)[1])
    end
    new_code.transform_shuffle(shuffle_arrays)
    
    # Use the local on-chip memory (conditionally do this)
    if transformation[0,1] == '1'
      local_memory_arrays = [@arrays.select(INPUT)[0]]
      new_code.transform_use_local_memory(local_memory_arrays)
    end
    
    # Flatten the arrays to 1D (always do this)
    @arrays.each do |array|
      new_code.transform_flatten(array)
    end
    
    # Perform array substitution a.k.a. register caching (conditionally do this)
    if @register_caching_enabled == 1
      @arrays.outputs.each do |array|
        if array.species.element?
          if @arrays.inputs.include?(array)
            new_code.transform_substitution(array,true)
          else
            new_code.transform_substitution(array,false)
          end
          extra_indent = INDENT
        end
      end
    end
    
    # Perform transformations for reduction operations (conditionally do this)
    if transformation[1,1].to_i >= 1
      input = @arrays.select(INPUT)[0]
      @arrays.select(OUTPUT).each do |output|
        if output.species.shared?
          new_code = new_code.transform_reduction(input,output,transformation[1,1].to_i)
        end
      end
    end
    
    # Perform thread-merging (experimental)
    # TODO: Solve the problem related to constants (e.g chunk/example1.c)
    if @merge_factor == 0
      if transformation[0,1] == '4' && @hash[:parallelism].to_i >= 1024*1024
        @merge_factor = 4
      else
        @merge_factor = 1
      end
    end
    if @merge_factor > 1
      #puts @hash[:parallelism]
      if new_code.has_conditional_statements?
        puts MESSAGE+'Not coarsening ('+@merge_factor.to_s+'x) because of conditional statements in kernel body.'
      # TODO: Fix this temporary hack for multiple loops with mismatching bounds
      elsif ((@hash[:parallelism].to_i % @merge_factor) != 0) || (@hash[:parallelism].to_i == 4192256)
        puts MESSAGE+'Not coarsening ('+@merge_factor.to_s+'x) because of mismatching amount of parallelism ('+@hash[:parallelism]+').'
      else
        puts MESSAGE+'Coarsening threads by a factor '+@merge_factor.to_s+'.'
        
        # Update the hash
        @hash[:ids] = @hash[:ids].split(NL).map { |line|
          C::parse(line).transform_merge_threads(@merge_factor,[GLOBAL_ID]+@constants.map{ |c| c.name }).to_s.split(NL).each_with_index.map do |id,index|
            id.gsub(/\b#{GLOBAL_ID}\b/,"(#{GLOBAL_ID}+gridDim.x*blockDim.x*#{index})")
          end
        }.join(NL+INDENT*2)
        @hash[:parallelism] = (@hash[:parallelism].to_i / @merge_factor).to_s
        
        # Transform the code
        excludes = (@constants+@arrays).map { |c| c.name }
        new_code.transform_merge_threads(@merge_factor,excludes)
      end
    end
    
    # Obtain the complexity in terms of operations for the resulting code
    complexity += new_code.get_complexity
    
    # Store the resulting code in the hash
    resulting_code = new_code.strip_brackets.to_s
    @hash[('algorithm_code'+(num_transformation+1).to_s).to_sym] = (transformation[1,1].to_i >= 1) ? resulting_code : extra_indent+INDENT+resulting_code.gsub!(NL,NL+INDENT)
  end
  
  @hash[:complexity] = complexity.to_s
end

#performance_model_code(model_dir) ⇒ Object

Method to generate performance modeling code. This method is still under construction and will not be called yet. TODO: Complete this method



556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
# File 'lib/bones/algorithm.rb', line 556

def performance_model_code(model_dir)
  
  # Load the profile database
  profiles = Array.new
  File.read(File.join(model_dir,'profile.txt')).each do |line|
    profiles.push(line.split(','))
  end
  
  # Iterate over all the profiles
  result = C::NodeArray.new
  profiles.each do |profile|
    
    # Fill the hash with profile information and species information
    mini_hash = {
      :name => profile[0].strip,
      :comp => profile[1].strip,
      :coal => profile[2].strip,
      :unco => profile[3].strip,
      :copy => profile[4].strip,
      :f    => @hash[:complexity],
      :w    => @hash[:parallelism],
      :c    => @species.all_structures.map { |s| simplify('4*('+s.dimensions.map { |d| sum(d) }.join('*')+')') }.join(' + '),
      :m    => '1',
      :u    => '0',
      :o    => '8'
    }
    
    # Load the skeleton for the performance model and set the values according to the hash
    model_skeleton = File.read(File.join(model_dir,'model.c'))
    search_and_replace!(mini_hash,model_skeleton)
    result.push(C::Block.parse(model_skeleton))
  end
  return result
end

#populate_hashObject

This method creates the search-and-replace hash based on information provided by the algorithm. It is called from the ‘populate’ method of this class.

List of possible hash keys:

algorithm_id

_name
_basename
_filename
_code*

(in*|out*)_type

_name
_devicename
_devicepointer
_dimensions
_dimension*_to
           _from
           _sum
_to
_from
_parameters
_parameter*_to
           _from
           _sum
_ids
_localids
_flatindex

(in|out)_names

_devicenames
_devicedefinitions
_devicedefinitionsopencl

names devicenames devicedefinitions devicedefinitionsopencl

parallelism factors ids verifyids

argument_name argument_definition kernel_argument_list



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/bones/algorithm.rb', line 248

def populate_hash
  @hash[:argument_name] = @lists[:argument_name]
  @hash[:argument_definition] = @lists[:argument_definition]
  
  # Obtain the necessary data for the hash per array
  parallelisms = []
  DIRECTIONS.each do |direction|
    arrays = @arrays.select(direction)
    arrays.each_with_index do |array,num_array|
      hashid = "#{direction}#{num_array}".to_sym
      
      # Gather the name and type data
      minihash = {:type          => array.type_name,
                  :name          => array.name,
                  :devicepointer => array.device_pointer,
                  :devicename    => array.device_name,
                  :flatindex     => array.flatindex}
      
      # Gather the dimensions data
      dimensions = array.species.dimensions
      dimensions.each_with_index do |dimension,num_dimension|
        minihash["dimension#{num_dimension}".to_sym] = {:sum  => simplify(sum(dimension)),
                                                        :from => simplify(from(dimension)),
                                                        :to   => simplify(to(dimension))}
      end
      minihash[:dimensions]  = simplify(dimensions.map { |d| sum(d) }.join('*'))
      minihash[:from] = dimensions.map { |d| from(d) }.zip(array.factors.drop(1).reverse).map { |e| simplify(e.join('')) }.join('+')
      minihash[:to  ] = dimensions.map { |d| to(d)   }.zip(array.factors.drop(1).reverse).map { |e| simplify(e.join('')) }.join('+')
      
      # Gather the parameter data
      if array.species.has_parameter?
        parameters = array.species.parameters
        parameters.each_with_index do |parameter,num_parameter|
          minihash["parameter#{num_parameter}".to_sym] = {:sum  => simplify(sum(parameter)),
                                                          :from => simplify(from(parameter)),
                                                          :to   => simplify(to(parameter))}
        end
        minihash[:parameters]  = simplify(parameters.map { |p| sum(p) }.join('*'))
      end
      
      # Store the data into the hash
      @hash[hashid] = minihash
      
      # Gather information regarding the parallelism
      if array.species.chunk?
        dim_div = simplify(minihash[:dimensions]+'/'+minihash[:parameters])
        parallelisms.push([dim_div,hashid,0])
      elsif array.species.element? || array.species.neighbourhood?
        parallelisms.push([minihash[:dimensions],hashid,1])
      end
      
      # Populate the global ID definitions hash, create the proper indices (and store as '{in/out}*_ids' in the hash)
      ids, localids, verifyids, factors = [], [], [], ['']
      dimensions = array.species.dimensions.clone
      dimensions.each_with_index do |dimension,num_dimension|
        index         =  (array.species.reverse?) ? num_dimension : array.species.dimensions.length-num_dimension-1
        index_reverse = !(array.species.reverse?) ? num_dimension : array.species.dimensions.length-num_dimension-1
        
        # Generate the index expressions
        divider = (array.species.chunk?) ? '/'+sum(array.species.parameters[index]) : ''
        dimensions_hash = (index == dimensions.length-1) ? '1' : dimensions.drop(index+1).map { |d| sum(d) }.join('*')
        dimensions_hash = simplify(dimensions_hash)
        dimensions_division = (dimensions_hash == '1') ? '' : '/('+dimensions_hash+')'
        minihash = {:dimensions1 => "#{GLOBAL_ID}#{dimensions_division}",
                    :dimensions2 => "#{LOCAL_ID }#{dimensions_division}",
                    :modulo      => (index_reverse != dimensions.length-1) ? '%('+simplify(sum(dimension)+divider)+')' : '',
                    :offset      => simplify(from(dimension))}
        expr_global = search_and_replace(minihash,"((<dimensions1>)<modulo>)+<offset>")
        expr_local  = search_and_replace(minihash,"((<dimensions2>)<modulo>)+<offset>")
        
        # Selectively push the ID definitions to the result array
        from = array.species.from_at(index)
        to = array.species.to_at(index)
        verifyids.push("const int #{GLOBAL_ID}_#{index_reverse} = "+expr_global+';')
        if from != to
          ids.push("const int #{GLOBAL_ID}_#{index_reverse} = "+expr_global+';')
          localids.push("const int #{LOCAL_ID }_#{index_reverse} = "+expr_local+';')
          factors.push(array.factors[index_reverse])
        end
      end
      
      # Store the results in the hash
      @hash[hashid][:ids] = ids.join(NL+INDENT*2)
      @hash[hashid][:localids] = localids.join(NL+INDENT*2)
      @hash[hashid][:verifyids] = verifyids.join(NL+INDENT*2)
      @hash[hashid][:factors] = factors.last
    end
    
    # Create lists of array names and definitions
    @hash["#{direction}_devicedefinitions".to_sym]       = arrays.map { |a| a.device_definition }.uniq.join(', ')
    @hash["#{direction}_devicedefinitionsopencl".to_sym] = arrays.map { |a| '__global '+a.device_definition }.uniq.join(', ')
    @hash["#{direction}_devicenames".to_sym]             = arrays.map { |a| a.device_name }.uniq.join(', ')
    @hash["#{direction}_names".to_sym]                   = arrays.map { |a| a.name }.uniq.join(', ')
  end
  @hash[:devicedefinitions]       = @arrays.map { |a| a.device_definition }.uniq.join(', ')
  @hash[:devicedefinitionsopencl] = @arrays.map { |a| '__global '+a.device_definition }.uniq.join(', ')
  @hash[:devicenames]             = @arrays.map { |a| a.device_name }.uniq.join(', ')
  @hash[:names]                   = @arrays.map { |a| a.name }.uniq.join(', ')
  
  # Set the parallelism for the complete species, first sort them according to priorities and then find the maximum
  # TODO: Remove the 'reverse' statement and get the 'ids' part working correctly for chunks
  # TODO: How to find the maximum of symbolic expressions?
  parallelisms = parallelisms.reverse.sort_by { |p| p[2] }
  parallelism = parallelisms.reverse.max_by { |p| p[0].to_i }
  @hash[:parallelism] = parallelism[0]
  @hash[:ids]         = @hash[parallelism[1]][:ids]
  @hash[:factors]     = @hash[parallelism[1]][:factors]
  @arrays.set_representative(parallelism[1])
end

#populate_listsObject

Method to populate 5 lists with variable information. Below are listed the names of the four lists with an example value:

host_name

Example: ‘array’

host_definition

Example: ‘int array[10]’

argument_name

Example: ‘threshold’

argument_definition

Example: ‘float threshold’

golden_name

Example: ‘golden_array’



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/bones/algorithm.rb', line 490

def populate_lists
  @constants.each do |variable|
    @lists[:host_name]          .push(variable.name)
    @lists[:host_definition]    .push(variable.definition)
    @lists[:argument_name]      .push(variable.name)
    @lists[:argument_definition].push(variable.definition)
    @lists[:golden_name]        .push(variable.name)
  end
  @arrays.each do |variable|
    @lists[:host_name]          .push(variable.name)
    @lists[:host_definition]    .push(variable.definition)
    @lists[:golden_name]        .push(variable.golden_name)
  end
  @lists.each { |name,list| @lists[name] = list.join(', ') }
end

#populate_variables(original_code, defines) ⇒ Object

Method to create a list of variables for the current algorithm. These variables should hold two conditions: 1) they are not local to the algorithm’s code, and 2), they are used in the algorithm’s code.

The method gets a lists of undefined variables in the algorithm’s code and subsequently searches the original code for the definition of this variable.



400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/bones/algorithm.rb', line 400

def populate_variables(original_code,defines)
  @code.undefined_variables.each do |name|
    type = @function_code.variable_type(name)
    raise_error('Variable '+name+' not declared in original code') if !type
    size = original_code.size(name)
    direction = @code.direction(name)
    size.map! { |s| simplify(replace_defines(s,defines)) }
    variable = Variable.new(name,type,size,direction,@id,@species.shared?)
    (variable.dimensions > 0) ? @arrays.push(variable) : @constants.push(variable)
  end
  raise_error('No input nor output arrays detected, make sure they are properly defined') if arrays.empty?
  
  DIRECTIONS.each do |direction|
    species = @species.structures(direction)
    if direction == INPUT && @species.shared?
      arrays = @arrays.inputs_only
    else
      arrays = @arrays.select(direction)
    end
    if !arrays.empty?
      
      # Check if the amount of input/ouput arrays is equal to the amount of input/output species
      if species.length < arrays.length
        array_names = arrays.map { |a| a.name }.join('","')
        raise_error(direction.capitalize+'put array count mismatch (expected '+species.length.to_s+', found '+arrays.length.to_s+' ["'+array_names+'"])')
      end

      # Set the species for the arrays (distinguish between arrays with and without a name)
      species.each do |structure|
        
        # Loop over all found arrays and match it with a species
        array = nil
        arrays.each do |free_array|
          if !free_array.species
            if structure.has_arrayname?
              if structure.name == free_array.name
                array = free_array
                break
              end
            else
              array = free_array
              break
            end
          end
        end
        
        # Still haven't found anything, assign the species to an array of equal name
        if !array
          arrays.each do |free_array|
            array = free_array if structure.name == free_array.name
          end
        end

        # Still haven't found anything, raise an error
        if !array
          raise_error("Could not find a matching array in C-code for a species with name '#{species.first.name}'")
        end
        
        # Process the assignment
        array.species = structure
        raise_error("Species of '#{array.species.name}' is mismatched with array '#{array.name}'") if array.species.name != array.name
        
        # Check if the array size was set, if not, it will be set to the species' size
        if array.size.empty?
          array.size = array.species.dimensions.map { |d| sum(d) }
          array.guess = true
          puts WARNING+'Could not determine size for array "'+array.name+'" automatically, assuming: '+array.size.inspect+'.'
        end
        
        # Set the multiplication factors (for later)
        array.set_factors
      end
    end
  end
  
  # Sort the arrays according to the alphabet
  if @arrays.length > 1
    @arrays.sort_by(['chunk','neighbourhood','element','shared','full'])
  end
end

#set_function(full_code) ⇒ Object

This method sets the code and name for the function in which the algorithm is found. This is done based on the original code, which is given as input to this method. The method does not return any value, instead, it sets two class variables (@function_code and @function_name).



60
61
62
63
64
65
66
67
68
# File 'lib/bones/algorithm.rb', line 60

def set_function(full_code)
  full_code.get_functions.each do |function|
    if function.node_exists?(@code)
      @function_code = function
      @function_name = function.name
    end
  end
  raise_error("Incorrect code found in body of #{@name}, something wrong with the classification?") if @function_code == ""
end

#update_hash(loop_variable) ⇒ Object

This method updates the hash after loops are removed from the code. It takes as an argument a loop variable, which it removes from both the ‘:argument_name’ and ‘:argument_ definition’ hash entries.



373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/bones/algorithm.rb', line 373

def update_hash(loop_variable)
  names = @hash[:argument_name].split(', ')
  definitions = @hash[:argument_definition].split(', ')
  # TODO: The following two lines give problems with correlation-k4
  names.delete(loop_variable.to_s)
  definitions.each { |definition| definitions.delete(definition) if definition =~ /\b#{loop_variable}\b/ }
  @hash[:argument_name] = names.join(', ')
  @hash[:argument_definition] = definitions.join(', ')
  
  # Now, generate the special code which is required for OpenCL function calls to be able to use kernel arguments.
  @hash[:kernel_argument_list] = opencl_arguments([@hash[:devicenames],@hash[:argument_name]].join(', ').remove_extras,0)
  @hash[:kernel_argument_list_in] = opencl_arguments(@hash[:in_devicenames],0)
  @hash[:kernel_argument_list_out] = opencl_arguments(@hash[:out_devicenames],0)
  @hash[:kernel_argument_list_constants] = opencl_arguments(@hash[:argument_name],0)
  
  # Add declarations for the loop variables for the original code in the hash
  @hash[:algorithm_code0] = INDENT+"int #{loop_variable};"+NL+@hash[:algorithm_code0]
end