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 = nil
	@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.



497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
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
# File 'lib/bones/algorithm.rb', line 497

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.



354
355
356
357
358
359
360
361
# File 'lib/bones/algorithm.rb', line 354

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.



78
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
# File 'lib/bones/algorithm.rb', line 78

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
			new_code = new_code.transform_reduction(@arrays.select(INPUT)[0],@arrays.select(OUTPUT)[0],transformation[1,1].to_i)
		end
		
		# Perform thread-merging (experimental)
		# TODO: Solve the problem related to constants (e.g chunk/example1.c)
		if @merge_factor == nil
			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



541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# File 'lib/bones/algorithm.rb', line 541

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



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
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
# File 'lib/bones/algorithm.rb', line 242

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’



475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'lib/bones/algorithm.rb', line 475

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.



394
395
396
397
398
399
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
# File 'lib/bones/algorithm.rb', line 394

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)
		arrays = @arrays.select(direction)
		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
				
				# 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
# 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
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.



367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/bones/algorithm.rb', line 367

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