kernel-tuner 0.3.1__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/PKG-INFO +11 -11
  2. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/README.rst +7 -7
  3. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/c.py +5 -3
  4. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/core.py +4 -3
  5. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/interface.py +5 -3
  6. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/runners/sequential.py +18 -5
  7. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/minimize.py +14 -5
  8. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/util.py +98 -13
  9. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/wrappers.py +1 -1
  10. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/PKG-INFO +11 -11
  11. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/SOURCES.txt +0 -1
  12. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/setup.py +1 -1
  13. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/test/test_c_functions.py +2 -2
  14. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/test/test_core.py +1 -1
  15. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/test/test_cuda_functions.py +3 -23
  16. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/test/test_cuda_mocked.py +1 -2
  17. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/test/test_interface.py +9 -9
  18. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/test/test_minimize.py +1 -0
  19. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/test/test_util_functions.py +39 -3
  20. kernel_tuner-0.3.1/kernel_tuner/cupy_backend.py +0 -331
  21. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/LICENSE +0 -0
  22. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/MANIFEST.in +0 -0
  23. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/__init__.py +0 -0
  24. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/cuda.py +0 -0
  25. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/kernelbuilder.py +0 -0
  26. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/nvml.py +0 -0
  27. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/opencl.py +0 -0
  28. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/runners/__init__.py +0 -0
  29. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/__init__.py +0 -0
  30. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/basinhopping.py +0 -0
  31. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/bayes_opt.py +0 -0
  32. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/brute_force.py +0 -0
  33. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/diff_evo.py +0 -0
  34. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/firefly_algorithm.py +0 -0
  35. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/genetic_algorithm.py +0 -0
  36. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/pso.py +0 -0
  37. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/random_sample.py +0 -0
  38. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner/strategies/simulated_annealing.py +0 -0
  39. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/dependency_links.txt +0 -0
  40. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/requires.txt +0 -0
  41. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/top_level.txt +0 -0
  42. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/setup.cfg +0 -0
  43. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/test/test_opencl_functions.py +0 -0
  44. {kernel_tuner-0.3.1 → kernel_tuner-0.3.2}/test/test_runners.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kernel_tuner
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: An easy to use CUDA/OpenCL kernel tuner in Python
5
5
  Home-page: http://benvanwerkhoven.github.io/kernel_tuner/
6
6
  Author: Ben van Werkhoven
@@ -9,7 +9,7 @@ License: Apache 2.0
9
9
  Description: Kernel Tuner
10
10
  ============
11
11
 
12
- |Build Status| |CodeCov Badge| |PyPi Badge|
12
+ |Build Status| |CodeCov Badge| |PyPi Badge| |Zenodo Badge| |SonarCloud Badge| |FairSoftware Badge|
13
13
 
14
14
  Kernel Tuner simplifies the software development of optimized and auto-tuned GPU programs, by enabling Python-based unit testing of GPU code and making it easy to develop scripts for auto-tuning GPU kernels. This also means no extensive changes and no new dependencies are required in the kernel code. The kernels can still be compiled and used as normal from any host programming language.
15
15
 
@@ -178,12 +178,12 @@ Description: Kernel Tuner
178
178
  :target: https://codecov.io/gh/benvanwerkhoven/kernel_tuner
179
179
  .. |PyPi Badge| image:: https://img.shields.io/pypi/v/kernel_tuner.svg?colorB=blue
180
180
  :target: https://pypi.python.org/pypi/kernel_tuner/
181
-
182
-
183
-
184
-
185
-
186
-
181
+ .. |Zenodo Badge| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.1220113.svg
182
+ :target: https://doi.org/10.5281/zenodo.1220113
183
+ .. |SonarCloud Badge| image:: https://sonarcloud.io/api/project_badges/measure?project=benvanwerkhoven_kernel_tuner&metric=alert_status
184
+ :target: https://sonarcloud.io/dashboard?id=benvanwerkhoven_kernel_tuner
185
+ .. |FairSoftware Badge| image:: https://img.shields.io/badge/fair--software.eu-%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F-green
186
+ :target: https://fair-software.eu
187
187
 
188
188
  Keywords: auto-tuning gpu computing pycuda cuda pyopencl opencl
189
189
  Platform: UNKNOWN
@@ -201,9 +201,9 @@ Classifier: Topic :: Scientific/Engineering
201
201
  Classifier: Topic :: Software Development
202
202
  Classifier: Topic :: System :: Distributed Computing
203
203
  Classifier: Development Status :: 4 - Beta
204
- Provides-Extra: dev
204
+ Provides-Extra: doc
205
205
  Provides-Extra: cuda
206
- Provides-Extra: tutorial
207
206
  Provides-Extra: opencl
208
207
  Provides-Extra: cuda_opencl
209
- Provides-Extra: doc
208
+ Provides-Extra: tutorial
209
+ Provides-Extra: dev
@@ -1,7 +1,7 @@
1
1
  Kernel Tuner
2
2
  ============
3
3
 
4
- |Build Status| |CodeCov Badge| |PyPi Badge|
4
+ |Build Status| |CodeCov Badge| |PyPi Badge| |Zenodo Badge| |SonarCloud Badge| |FairSoftware Badge|
5
5
 
6
6
  Kernel Tuner simplifies the software development of optimized and auto-tuned GPU programs, by enabling Python-based unit testing of GPU code and making it easy to develop scripts for auto-tuning GPU kernels. This also means no extensive changes and no new dependencies are required in the kernel code. The kernels can still be compiled and used as normal from any host programming language.
7
7
 
@@ -170,9 +170,9 @@ Cedric Nugteren. CLTune is a C++ library for kernel tuning.
170
170
  :target: https://codecov.io/gh/benvanwerkhoven/kernel_tuner
171
171
  .. |PyPi Badge| image:: https://img.shields.io/pypi/v/kernel_tuner.svg?colorB=blue
172
172
  :target: https://pypi.python.org/pypi/kernel_tuner/
173
-
174
-
175
-
176
-
177
-
178
-
173
+ .. |Zenodo Badge| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.1220113.svg
174
+ :target: https://doi.org/10.5281/zenodo.1220113
175
+ .. |SonarCloud Badge| image:: https://sonarcloud.io/api/project_badges/measure?project=benvanwerkhoven_kernel_tuner&metric=alert_status
176
+ :target: https://sonarcloud.io/dashboard?id=benvanwerkhoven_kernel_tuner
177
+ .. |FairSoftware Badge| image:: https://img.shields.io/badge/fair--software.eu-%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F-green
178
+ :target: https://fair-software.eu
@@ -90,19 +90,21 @@ class CFunctions(object):
90
90
  if not isinstance(arg, (numpy.ndarray, numpy.number)):
91
91
  raise TypeError("Argument is not numpy ndarray or numpy scalar %s" % type(arg))
92
92
  dtype_str = str(arg.dtype)
93
- data = arg.copy()
93
+ #data = arg.copy()
94
94
  if isinstance(arg, numpy.ndarray):
95
95
  if dtype_str in dtype_map.keys():
96
96
  # In numpy <= 1.15, ndarray.ctypes.data_as does not itself keep a reference
97
97
  # to its underlying array, so we need to store a reference to arg.copy()
98
98
  # in the Argument object manually to avoid it being deleted.
99
99
  # (This changed in numpy > 1.15.)
100
- data_ctypes = data.ctypes.data_as(C.POINTER(dtype_map[dtype_str]))
100
+ #data_ctypes = data.ctypes.data_as(C.POINTER(dtype_map[dtype_str]))
101
+ data_ctypes = arg.ctypes.data_as(C.POINTER(dtype_map[dtype_str]))
101
102
  else:
102
103
  raise TypeError("unknown dtype for ndarray")
103
104
  elif isinstance(arg, numpy.generic):
104
105
  data_ctypes = dtype_map[dtype_str](arg)
105
- ctype_args[i] = Argument(numpy=data, ctypes=data_ctypes)
106
+ #ctype_args[i] = Argument(numpy=data, ctypes=data_ctypes)
107
+ ctype_args[i] = Argument(numpy=arg, ctypes=data_ctypes)
106
108
  return ctype_args
107
109
 
108
110
  def compile(self, kernel_instance):
@@ -115,7 +115,7 @@ class KernelSource(object):
115
115
 
116
116
  ks = self.get_kernel_string(i, params)
117
117
  # add preprocessor statements
118
- n, ks = util.prepare_kernel_string(kernel_name, ks, params, grid, threads, block_size_names)
118
+ n, ks = util.prepare_kernel_string(kernel_name, ks, params, grid, threads, block_size_names, self.lang)
119
119
 
120
120
  if i == 0:
121
121
  # primary kernel source
@@ -175,7 +175,7 @@ class KernelSource(object):
175
175
  class DeviceInterface(object):
176
176
  """Class that offers a High-Level Device Interface to the rest of the Kernel Tuner"""
177
177
 
178
- def __init__(self, kernel_source, device=0, platform=0, lang=None, quiet=False, compiler=None, compiler_options=None, iterations=7):
178
+ def __init__(self, kernel_source, device=0, platform=0, quiet=False, compiler=None, compiler_options=None, iterations=7):
179
179
  """ Instantiate the DeviceInterface, based on language in kernel source
180
180
 
181
181
  :param kernel_source The kernel sources
@@ -349,7 +349,8 @@ class DeviceInterface(object):
349
349
  #compiles may fail because certain kernel configurations use too
350
350
  #much shared memory for example, the desired behavior is to simply
351
351
  #skip over this configuration and try the next one
352
- if "uses too much shared data" in str(e):
352
+ shared_mem_error_messages = ["uses too much shared data", "local memory limit exceeded"]
353
+ if any(msg in str(e) for msg in shared_mem_error_messages):
353
354
  logging.debug('compile_kernel failed due to kernel using too much shared memory')
354
355
  if verbose:
355
356
  print("skipping config", instance.name, "reason: too much shared memory used")
@@ -318,7 +318,8 @@ _tuning_options = Options([
318
318
  ("cache", ("""filename for caching/logging benchmarked instances
319
319
  filename uses suffix ".json"
320
320
  if the file exists it is read and tuning continues from this file
321
- """, "string"))
321
+ """, "string")),
322
+ ("metrics", ("specifies user-defined metrics", "OrderedDict"))
322
323
  ])
323
324
 
324
325
  _device_options = Options([
@@ -366,7 +367,7 @@ def tune_kernel(kernel_name, kernel_string, problem_size, arguments,
366
367
  lang=None, device=0, platform=0, smem_args=None, cmem_args=None, texmem_args=None,
367
368
  compiler=None, compiler_options=None, log=None,
368
369
  iterations=7, block_size_names=None, quiet=False, strategy=None, strategy_options=None,
369
- cache=None):
370
+ cache=None, metrics=None):
370
371
 
371
372
  if log:
372
373
  logging.basicConfig(filename=kernel_name + datetime.now().strftime('%Y%m%d-%H:%M:%S') + '.log', level=log)
@@ -450,7 +451,8 @@ def tune_kernel(kernel_name, kernel_string, problem_size, arguments,
450
451
  if results: #checks if results is not empty
451
452
  best_config = min(results, key=lambda x: x['time'])
452
453
  units = getattr(runner, "units", None)
453
- print("best performing configuration:", util.get_config_string(best_config, list(tune_params.keys()) + ['time'], units=units))
454
+ print("best performing configuration:")
455
+ util.print_config_output(tune_params, best_config, device_options.quiet, metrics, units)
454
456
  else:
455
457
  print("no results to report")
456
458
 
@@ -4,7 +4,7 @@ from __future__ import print_function
4
4
  from collections import OrderedDict
5
5
  import logging
6
6
 
7
- from kernel_tuner.util import get_config_string, store_cache
7
+ from kernel_tuner.util import get_config_string, store_cache, process_metrics, print_config_output
8
8
  from kernel_tuner.core import DeviceInterface
9
9
 
10
10
 
@@ -35,6 +35,8 @@ class SequentialRunner(object):
35
35
  self.quiet = device_options.quiet
36
36
  self.kernel_source = kernel_source
37
37
 
38
+ self.warmed_up = False
39
+
38
40
  #move data to the GPU
39
41
  self.gpu_args = self.dev.ready_argument_list(kernel_options.arguments)
40
42
 
@@ -66,6 +68,11 @@ class SequentialRunner(object):
66
68
  for element in parameter_space:
67
69
  params = OrderedDict(zip(tuning_options.tune_params.keys(), element))
68
70
 
71
+ #attempt to warmup the GPU by running the first config in the parameter space and ignoring the result
72
+ if not self.warmed_up:
73
+ self.dev.compile_and_benchmark(self.kernel_source, self.gpu_args, params, kernel_options, tuning_options)
74
+ self.warmed_up = True
75
+
69
76
  #check if element is in the cache
70
77
  x_int = ",".join([str(i) for i in element])
71
78
  if tuning_options.cache:
@@ -87,14 +94,16 @@ class SequentialRunner(object):
87
94
  time = result
88
95
 
89
96
  params['time'] = time
90
- output_string = get_config_string(params, params.keys(), self.units)
91
- logging.debug(output_string)
92
- if not self.quiet:
93
- print(output_string)
94
97
 
95
98
  if isinstance(result, dict):
96
99
  params.update(result)
97
100
 
101
+
102
+ if tuning_options.metrics:
103
+ params = process_metrics(params, tuning_options.metrics)
104
+
105
+ print_config_output(tuning_options.tune_params, params, self.quiet, tuning_options.metrics, self.units)
106
+
98
107
  store_cache(x_int, params, tuning_options)
99
108
  results.append(params)
100
109
 
@@ -104,3 +113,7 @@ class SequentialRunner(object):
104
113
  def __del__(self):
105
114
  if hasattr(self, 'dev'):
106
115
  del self.dev
116
+
117
+
118
+
119
+
@@ -98,19 +98,28 @@ def _cost_func(x, kernel_options, tuning_options, runner, results):
98
98
 
99
99
  def get_bounds_x0_eps(tuning_options):
100
100
  """compute bounds, x0 (the initial guess), and eps"""
101
- values = tuning_options.tune_params.values()
101
+ values = list(tuning_options.tune_params.values())
102
+
103
+ if "x0" in tuning_options.strategy_options:
104
+ x0 = tuning_options.strategy_options.x0
105
+ else:
106
+ x0 = None
102
107
 
103
108
  if tuning_options.scaling:
104
- #bounds = [(0, 1) for _ in values]
105
- #x0 = [0.5 for _ in bounds]
106
109
  eps = numpy.amin([1.0/len(v) for v in values])
107
110
 
108
111
  #reducing interval from [0, 1] to [0, eps*len(v)]
109
112
  bounds = [(0, eps*len(v)) for v in values]
110
- x0 = [0.5*eps*len(v) for v in values]
113
+ if x0:
114
+ #x0 has been supplied by the user, map x0 into [0, eps*len(v)]
115
+ for i, e in enumerate(values):
116
+ x0[i] = eps*values[i].index(x0[i])
117
+ else:
118
+ x0 = [0.5*eps*len(v) for v in values]
111
119
  else:
112
120
  bounds = get_bounds(tuning_options.tune_params)
113
- x0 = [(min_v+max_v)/2.0 for (min_v, max_v) in bounds]
121
+ if not x0:
122
+ x0 = [(min_v+max_v)/2.0 for (min_v, max_v) in bounds]
114
123
  eps = 1e9
115
124
  for v_list in values:
116
125
  vals = numpy.sort(v_list)
@@ -10,7 +10,7 @@ import logging
10
10
  import warnings
11
11
  import re
12
12
 
13
- import numpy
13
+ import numpy as np
14
14
 
15
15
  default_block_size_names = ["block_size_x", "block_size_y", "block_size_z"]
16
16
 
@@ -36,7 +36,7 @@ def check_argument_list(kernel_name, kernel_string, args):
36
36
  """ raise an exception if a kernel arguments do not match host arguments """
37
37
  kernel_arguments = list()
38
38
  collected_errors = list()
39
- for iterator in re.finditer(kernel_name + "[ \n\t]*" + "\(", kernel_string):
39
+ for iterator in re.finditer(kernel_name + "[ \n\t]*" + r"\(", kernel_string):
40
40
  kernel_start = iterator.end()
41
41
  kernel_end = kernel_string.find(")", kernel_start)
42
42
  if kernel_start != 0:
@@ -49,11 +49,11 @@ def check_argument_list(kernel_name, kernel_string, args):
49
49
  for (i, arg) in enumerate(args):
50
50
  kernel_argument = arguments[i]
51
51
 
52
- if not isinstance(arg, (numpy.ndarray, numpy.generic)):
53
- raise TypeError("Argument at position " + str(i) + " of type: " + str(type(arg)) + " should be of type numpy.ndarray or numpy scalar")
52
+ if not isinstance(arg, (np.ndarray, np.generic)):
53
+ raise TypeError("Argument at position " + str(i) + " of type: " + str(type(arg)) + " should be of type np.ndarray or numpy scalar")
54
54
 
55
55
  correct = True
56
- if isinstance(arg, numpy.ndarray) and not "*" in kernel_argument:
56
+ if isinstance(arg, np.ndarray) and not "*" in kernel_argument:
57
57
  correct = False #array is passed to non-pointer kernel argument
58
58
 
59
59
  if correct and check_argument_type(str(arg.dtype), kernel_argument):
@@ -131,6 +131,12 @@ def detect_language(kernel_string):
131
131
 
132
132
  def get_config_string(params, keys=None, units=None):
133
133
  """ return a compact string representation of a measurement """
134
+ def compact_number(v):
135
+ if isinstance(v, float):
136
+ return "{:.3f}".format(round(v, 3))
137
+ else:
138
+ return str(v)
139
+
134
140
  compact_str_items = []
135
141
  if not keys:
136
142
  keys = params.keys()
@@ -140,7 +146,7 @@ def get_config_string(params, keys=None, units=None):
140
146
  unit = ""
141
147
  if isinstance(units, dict): #check if not None not enough, units could be mocked which causes errors
142
148
  unit = units.get(k, "")
143
- compact_str_items.append(k + "=" + str(v) + unit)
149
+ compact_str_items.append(k + "=" + compact_number(v) + unit)
144
150
  # and finally join them
145
151
  compact_str = ", ".join(compact_str_items)
146
152
  return compact_str
@@ -154,9 +160,9 @@ def get_grid_dimensions(current_problem_size, params, grid_div, block_size_names
154
160
  divisor_list = [default]
155
161
  else:
156
162
  return 1
157
- return numpy.prod([int(eval(replace_param_occurrences(s, params))) for s in divisor_list])
163
+ return np.prod([int(eval(replace_param_occurrences(s, params))) for s in divisor_list])
158
164
  divisors = [get_dimension_divisor(d, block_size_names[i], params) for i, d in enumerate(grid_div)]
159
- return tuple(int(numpy.ceil(float(current_problem_size[i]) / float(d))) for i, d in enumerate(divisors))
165
+ return tuple(int(np.ceil(float(current_problem_size[i]) / float(d))) for i, d in enumerate(divisors))
160
166
 
161
167
  def get_instance_string(params):
162
168
  """ combine the parameters to a string mostly used for debug output
@@ -206,13 +212,13 @@ def get_kernel_string(kernel_source, params=None):
206
212
 
207
213
  def get_problem_size(problem_size, params):
208
214
  """compute current problem size"""
209
- if isinstance(problem_size, (str, int, numpy.integer)):
215
+ if isinstance(problem_size, (str, int, np.integer)):
210
216
  problem_size = (problem_size, )
211
217
  current_problem_size = [1, 1, 1]
212
218
  for i, s in enumerate(problem_size):
213
219
  if isinstance(s, str):
214
220
  current_problem_size[i] = int(eval(replace_param_occurrences(s, params)))
215
- elif isinstance(s, (int, numpy.integer)):
221
+ elif isinstance(s, (int, np.integer)):
216
222
  current_problem_size[i] = s
217
223
  else:
218
224
  raise TypeError("Error: problem_size should only contain strings or integers")
@@ -234,6 +240,60 @@ def get_thread_block_dimensions(params, block_size_names=None):
234
240
  block_size_z = params.get(block_size_names[2], 1)
235
241
  return (int(block_size_x), int(block_size_y), int(block_size_z))
236
242
 
243
+ def print_config_output(tune_params, params, quiet, metrics, units):
244
+ """print the configuration string with tunable parameters and benchmark results"""
245
+ print_keys = list(tune_params.keys())+["time"]
246
+ if metrics:
247
+ print_keys += metrics.keys()
248
+ output_string = get_config_string(params, print_keys, units)
249
+ if not quiet:
250
+ print(output_string)
251
+
252
+ def process_metrics(params, metrics):
253
+ """ process user-defined metrics for derived benchmark results
254
+
255
+ Metrics must be an OrderedDict to support composable metrics. The dictionary keys describe
256
+ the name given to this user-defined metric and will be used as the key in the results dictionaries
257
+ return by Kernel Tuner. The values describe how to calculate the user-defined metric, using either a
258
+ string expression in which the tunable parameters and benchmark results can be used as variables, or
259
+ as a function that accepts a dictionary as argument.
260
+ Example:
261
+ metrics = OrderedDict()
262
+ metrics["x"] = "10000 / time"
263
+ metrics["x2"] = "x*x"
264
+
265
+ Note that the values in the metric dictionary can also be functions that accept params as argument.
266
+ Example:
267
+ metrics = OrderedDict()
268
+ metrics["GFLOP/s"] = lambda p : 10000 / p["time"]
269
+
270
+ :param params: A dictionary with tunable parameters and benchmark results.
271
+ :type params: dict
272
+
273
+ :param metrics: An OrderedDict with user-defined metrics that can be used to create derived benchmark results.
274
+ :type metrics: OrderedDict
275
+
276
+ :returns: An updated params dictionary with the derived metrics inserted along with the benchmark results.
277
+ :rtype: dict
278
+
279
+ """
280
+ if not isinstance(metrics, OrderedDict):
281
+ raise ValueError("metrics should be an OrderedDict to preserve order and support composability")
282
+ for k, v in metrics.items():
283
+ if isinstance(v, str):
284
+ value = eval(replace_param_occurrences(v, params))
285
+ elif callable(v):
286
+ value = v(params)
287
+ else:
288
+ raise ValueError("metric dicts values should be strings or callable")
289
+ if not k in params:
290
+ params[k] = value
291
+ else:
292
+ raise ValueError("metric dicts keys should not already exist in params")
293
+ return params
294
+
295
+
296
+
237
297
  def looks_like_a_filename(kernel_source):
238
298
  """ attempt to detect whether source code or a filename was passed """
239
299
  logging.debug('looks_like_a_filename called')
@@ -256,7 +316,7 @@ def looks_like_a_filename(kernel_source):
256
316
  logging.debug('kernel_source is a filename: %s' % str(result))
257
317
  return result
258
318
 
259
- def prepare_kernel_string(kernel_name, kernel_string, params, grid, threads, block_size_names):
319
+ def prepare_kernel_string(kernel_name, kernel_string, params, grid, threads, block_size_names, lang):
260
320
  """ prepare kernel string for compilation
261
321
 
262
322
  Prepends the kernel with a series of C preprocessor defines specific
@@ -298,11 +358,24 @@ def prepare_kernel_string(kernel_name, kernel_string, params, grid, threads, blo
298
358
  for i, g in enumerate(threads):
299
359
  kernel_string = "#define " + block_size_names[i] + " " + str(g) + "\n" + kernel_string
300
360
  for k, v in params.items():
301
- if k not in block_size_names:
361
+ if "loop_unroll_factor" in k and lang == "CUDA":
362
+ #this handles the special case that in CUDA
363
+ #pragma unroll loop_unroll_factor, loop_unroll_factor should be a constant integer expression
364
+ #in OpenCL this isn't the case and we can just insert "#define loop_unroll_factor N"
365
+ if v > 0: # using 0 to disable loop unrolling for this loop
366
+ kernel_string = "const int " + k + " = " + str(v) + ";\n" + kernel_string
367
+ else:
368
+ kernel_string = re.sub(r"\n\s*#pragma\s+unroll\s+" + k, "\n", kernel_string) # + r"[^\S]*"
369
+ elif k not in block_size_names:
302
370
  kernel_string = "#define " + k + " " + str(v) + "\n" + kernel_string
371
+
303
372
  name = kernel_name
373
+
374
+ #also insert kernel_tuner token
375
+ kernel_string = "#define kernel_tuner 1\n" + kernel_string
304
376
  #name = kernel_name + "_" + get_instance_string(params)
305
377
  #kernel_string = kernel_string.replace(kernel_name, name)
378
+
306
379
  return name, kernel_string
307
380
 
308
381
  def read_file(filename):
@@ -467,11 +540,23 @@ def close_cache(cache):
467
540
 
468
541
 
469
542
  def store_cache(key, params, tuning_options):
543
+ """ stores a new entry (key, params) to the cachefile """
544
+ # create converter for dumping numpy objects to JSON
545
+ def npconverter(obj):
546
+ if isinstance(obj, np.integer):
547
+ return int(obj)
548
+ elif isinstance(obj, np.floating):
549
+ return float(obj)
550
+ elif isinstance(obj, np.ndarray):
551
+ return obj.tolist()
552
+ else:
553
+ return obj.__str__()
554
+
470
555
  logging.debug('store_cache called, cache=%s, cachefile=%s' % (tuning_options.cache, tuning_options.cachefile))
471
556
  if isinstance(tuning_options.cache, dict):
472
557
  if not key in tuning_options.cache:
473
558
  tuning_options.cache[key] = params
474
559
  if tuning_options.cachefile:
475
560
  with open(tuning_options.cachefile, "a") as cachefile:
476
- cachefile.write("\n" + json.dumps({key: params})[1:-1] + ",")
561
+ cachefile.write("\n" + json.dumps({key: params}, default=npconverter)[1:-1] + ",")
477
562
 
@@ -90,7 +90,7 @@ def cpp(function_name, kernel_source, args, convert_to_array=None):
90
90
  for i, arg in enumerate(args):
91
91
  if convert_to_array[i]:
92
92
  if not isinstance(arg, np.ndarray):
93
- ValueError("conversion to array reference only supported for arguments that are numpy arrays, use length-1 numpy array to pass a scalar by reference")
93
+ raise ValueError("conversion to array reference only supported for arguments that are numpy arrays, use length-1 numpy array to pass a scalar by reference")
94
94
  if np.prod(arg.shape) > 1:
95
95
  #convert pointer to a reference to an array
96
96
  arg_shape = "".join("[%d]" % i for i in arg.shape)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kernel-tuner
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: An easy to use CUDA/OpenCL kernel tuner in Python
5
5
  Home-page: http://benvanwerkhoven.github.io/kernel_tuner/
6
6
  Author: Ben van Werkhoven
@@ -9,7 +9,7 @@ License: Apache 2.0
9
9
  Description: Kernel Tuner
10
10
  ============
11
11
 
12
- |Build Status| |CodeCov Badge| |PyPi Badge|
12
+ |Build Status| |CodeCov Badge| |PyPi Badge| |Zenodo Badge| |SonarCloud Badge| |FairSoftware Badge|
13
13
 
14
14
  Kernel Tuner simplifies the software development of optimized and auto-tuned GPU programs, by enabling Python-based unit testing of GPU code and making it easy to develop scripts for auto-tuning GPU kernels. This also means no extensive changes and no new dependencies are required in the kernel code. The kernels can still be compiled and used as normal from any host programming language.
15
15
 
@@ -178,12 +178,12 @@ Description: Kernel Tuner
178
178
  :target: https://codecov.io/gh/benvanwerkhoven/kernel_tuner
179
179
  .. |PyPi Badge| image:: https://img.shields.io/pypi/v/kernel_tuner.svg?colorB=blue
180
180
  :target: https://pypi.python.org/pypi/kernel_tuner/
181
-
182
-
183
-
184
-
185
-
186
-
181
+ .. |Zenodo Badge| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.1220113.svg
182
+ :target: https://doi.org/10.5281/zenodo.1220113
183
+ .. |SonarCloud Badge| image:: https://sonarcloud.io/api/project_badges/measure?project=benvanwerkhoven_kernel_tuner&metric=alert_status
184
+ :target: https://sonarcloud.io/dashboard?id=benvanwerkhoven_kernel_tuner
185
+ .. |FairSoftware Badge| image:: https://img.shields.io/badge/fair--software.eu-%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F-green
186
+ :target: https://fair-software.eu
187
187
 
188
188
  Keywords: auto-tuning gpu computing pycuda cuda pyopencl opencl
189
189
  Platform: UNKNOWN
@@ -201,9 +201,9 @@ Classifier: Topic :: Scientific/Engineering
201
201
  Classifier: Topic :: Software Development
202
202
  Classifier: Topic :: System :: Distributed Computing
203
203
  Classifier: Development Status :: 4 - Beta
204
- Provides-Extra: dev
204
+ Provides-Extra: doc
205
205
  Provides-Extra: cuda
206
- Provides-Extra: tutorial
207
206
  Provides-Extra: opencl
208
207
  Provides-Extra: cuda_opencl
209
- Provides-Extra: doc
208
+ Provides-Extra: tutorial
209
+ Provides-Extra: dev
@@ -7,7 +7,6 @@ kernel_tuner/__init__.py
7
7
  kernel_tuner/c.py
8
8
  kernel_tuner/core.py
9
9
  kernel_tuner/cuda.py
10
- kernel_tuner/cupy_backend.py
11
10
  kernel_tuner/interface.py
12
11
  kernel_tuner/kernelbuilder.py
13
12
  kernel_tuner/nvml.py
@@ -14,7 +14,7 @@ else:
14
14
 
15
15
  setup(
16
16
  name="kernel_tuner",
17
- version="0.3.1",
17
+ version="0.3.2",
18
18
  author="Ben van Werkhoven",
19
19
  author_email="b.vanwerkhoven@esciencecenter.nl",
20
20
  description=("An easy to use CUDA/OpenCL kernel tuner in Python"),
@@ -89,9 +89,9 @@ def test_ready_argument_list5():
89
89
 
90
90
  assert all(output[0].numpy == arg1)
91
91
 
92
- # test that a copy has been made
92
+ # test that a copy has not been made
93
93
  arg1[0] = arg1[0] + 1
94
- assert not all(output[0].numpy == arg1)
94
+ assert all(output[0].numpy == arg1)
95
95
 
96
96
 
97
97
  def test_byte_array_arguments():
@@ -39,7 +39,7 @@ def env():
39
39
  verbose = True
40
40
  kernel_options = Options(kernel_name="vector_add", kernel_string=kernel_string, problem_size=args[-1], arguments=args, lang=lang,
41
41
  grid_div_x=None, grid_div_y=None, grid_div_z=None, cmem_args=None, texmem_args=None, block_size_names=None)
42
- device_options = Options(device=0, platform=0, lang=lang, quiet=False, compiler=None, compiler_options=None)
42
+ device_options = Options(device=0, platform=0, quiet=False, compiler=None, compiler_options=None)
43
43
  dev = core.DeviceInterface(kernel_source, iterations=7, **device_options)
44
44
  instance = dev.create_kernel_instance(kernel_source, kernel_options, params, verbose)
45
45
 
@@ -29,38 +29,18 @@ def test_ready_argument_list():
29
29
  @skip_if_no_cuda
30
30
  def test_compile():
31
31
 
32
- original_kernel = """
32
+ kernel_string = """
33
33
  __global__ void vector_add(float *c, float *a, float *b, int n) {
34
- __shared__ float test[shared_size];
35
34
  int i = blockIdx.x * blockDim.x + threadIdx.x;
36
35
  if (i<n) {
37
- test[0] = a[i];
38
- c[i] = test[0] + b[i];
36
+ c[i] = a[i] + b[i];
39
37
  }
40
38
  }
41
39
  """
42
40
 
43
- kernel_sources = KernelSource(original_kernel, "cuda")
44
-
45
-
46
- kernel_string = original_kernel.replace("shared_size", str(100*1024*1024))
41
+ kernel_sources = KernelSource(kernel_string, "cuda")
47
42
  kernel_instance = KernelInstance("vector_add", kernel_sources, kernel_string, [], None, None, dict(), [])
48
-
49
43
  dev = cuda.CudaFunctions(0)
50
- try:
51
- func = dev.compile(kernel_instance)
52
- assert isinstance(func, pycuda.driver.Function)
53
- print("Expected an exception because too much shared memory is requested")
54
- assert False
55
- except Exception as e:
56
- if "uses too much shared data" in str(e):
57
- assert True
58
- else:
59
- print("Expected a different exception:" + str(e))
60
- assert False
61
-
62
- kernel_string = original_kernel.replace("shared_size", str(100))
63
- kernel_instance = KernelInstance("vector_add", kernel_sources, kernel_string, [], None, None, dict(), [])
64
44
  try:
65
45
  func = dev.compile( kernel_instance)
66
46
  assert True
@@ -81,8 +81,7 @@ def test_benchmark(drv, *args):
81
81
  drv.Event.return_value.time_since.return_value = 0.1
82
82
 
83
83
  dev = cuda.CudaFunctions(0)
84
- args = [1, 2]
85
- res = dev.benchmark(dummy_func, args, (1,2), (1,2))
84
+ res = dev.benchmark(dummy_func, [1,2], (1,2), (1,2))
86
85
  assert res["time"] > 0
87
86
 
88
87
  assert dev.context.synchronize.call_count == 1
@@ -30,8 +30,8 @@ def test_interface_calls_functions(dev_interface):
30
30
  kernel_name, kernel_string, size, args, tune_params = get_fake_kernel()
31
31
  tune_kernel(kernel_name, kernel_string, size, args, tune_params, verbose=True)
32
32
 
33
- dev.compile.assert_called_once()
34
- dev.benchmark.assert_called_once_with('compile', 'ready_argument_list', (128, 1, 1), (10, 1, 1))
33
+ dev.compile.assert_called()
34
+ dev.benchmark.assert_called_with('compile', 'ready_argument_list', (128, 1, 1), (10, 1, 1))
35
35
 
36
36
  @patch('kernel_tuner.core.CudaFunctions')
37
37
  def test_interface_handles_max_threads(dev_interface):
@@ -45,7 +45,7 @@ def test_interface_handles_max_threads(dev_interface):
45
45
  tune_kernel("fake_kernel", kernel_string, (1,1), [numpy.int32(0)], tune_params, lang="CUDA")
46
46
 
47
47
  # verify that only a single instance of the kernel is compiled
48
- dev.compile.assert_called_once()
48
+ dev.compile.assert_called()
49
49
 
50
50
  @patch('kernel_tuner.core.CudaFunctions')
51
51
  def test_interface_handles_compile_error(dev_interface):
@@ -58,7 +58,7 @@ def test_interface_handles_compile_error(dev_interface):
58
58
  kernel_string = "__global__ void fake_kernel(int number)"
59
59
  tune_kernel("fake_kernel", kernel_string, (1,1), [numpy.int32(0)], tune_params, lang="CUDA")
60
60
 
61
- assert dev.compile.call_count == 1
61
+ assert dev.compile.call_count == 2
62
62
  assert dev.benchmark.called == False
63
63
 
64
64
  @patch('kernel_tuner.core.CudaFunctions')
@@ -72,8 +72,8 @@ def test_interface_handles_restriction(dev_interface):
72
72
  kernel_string = "__global__ void fake_kernel(int number)"
73
73
  tune_kernel("fake_kernel", kernel_string, (1,1), [numpy.int32(0)], tune_params, restrictions=restrict, lang="CUDA", verbose=True)
74
74
 
75
- assert dev.compile.call_count == 1
76
- dev.benchmark.assert_called_once_with('compile', 'ready_argument_list', (256, 1, 1), (1, 1, 1))
75
+ assert dev.compile.call_count == 2
76
+ dev.benchmark.assert_called_with('compile', 'ready_argument_list', (256, 1, 1), (1, 1, 1))
77
77
 
78
78
  @patch('kernel_tuner.core.CudaFunctions')
79
79
  def test_interface_handles_runtime_error(dev_interface):
@@ -86,8 +86,8 @@ def test_interface_handles_runtime_error(dev_interface):
86
86
  kernel_string = "__global__ void fake_kernel(int number)"
87
87
  results, _ = tune_kernel("fake_kernel",kernel_string, (1,1), [numpy.int32(0)], tune_params, lang="CUDA")
88
88
 
89
- assert dev.compile.call_count == 1
90
- dev.benchmark.assert_called_once_with('compile', 'ready_argument_list', (256, 1, 1), (1, 1, 1))
89
+ assert dev.compile.call_count == 2
90
+ dev.benchmark.assert_called_with('compile', 'ready_argument_list', (256, 1, 1), (1, 1, 1))
91
91
  assert len(results) == 0
92
92
 
93
93
  @patch('kernel_tuner.core.CudaFunctions')
@@ -99,6 +99,6 @@ def test_run_kernel(dev_interface):
99
99
  answer = run_kernel(kernel_name, kernel_string, size, args, {"block_size_x": 128})
100
100
 
101
101
  assert dev.compile.call_count == 1
102
- dev.run_kernel.assert_called_once_with('compile', 'ready_argument_list', (128, 1, 1), (10, 1, 1))
102
+ dev.run_kernel.assert_called_with('compile', 'ready_argument_list', (128, 1, 1), (10, 1, 1))
103
103
  assert answer[0] == size
104
104
 
@@ -16,6 +16,7 @@ def test_get_bounds_x0_eps():
16
16
  tune_params['x'] = [0, 1, 2, 3, 4]
17
17
 
18
18
  tuning_options["tune_params"] = tune_params
19
+ tuning_options["strategy_options"] = {}
19
20
 
20
21
  bounds, x0, eps = minimize.get_bounds_x0_eps(tuning_options)
21
22
 
@@ -126,8 +126,9 @@ def test_prepare_kernel_string():
126
126
  params = dict()
127
127
  params["is"] = 8
128
128
 
129
- _, output = prepare_kernel_string("this", kernel, params, (3,7), (1,2,3), block_size_names)
130
- expected = "#define is 8\n" \
129
+ _, output = prepare_kernel_string("this", kernel, params, (3,7), (1,2,3), block_size_names, "")
130
+ expected = "#define kernel_tuner 1\n" \
131
+ "#define is 8\n" \
131
132
  "#define block_size_z 3\n" \
132
133
  "#define block_size_y 2\n" \
133
134
  "#define block_size_x 1\n" \
@@ -454,7 +455,7 @@ def test_process_cache():
454
455
  assert len(tuning_options.cache) == 0
455
456
 
456
457
  #store one entry in the cache
457
- params = {"x": 4, "time": 0.1234}
458
+ params = {"x": 4, "time": numpy.float32(0.1234)}
458
459
  store_cache("4", params, tuning_options)
459
460
  assert len(tuning_options.cache) == 1
460
461
 
@@ -487,3 +488,38 @@ def test_process_cache():
487
488
  finally:
488
489
  delete_temp_file(cache)
489
490
  #pass
491
+
492
+
493
+ def test_process_metrics():
494
+ params = {"x": 15, "b": 12}
495
+ metrics = OrderedDict()
496
+ metrics["y"] = lambda p : p["x"]
497
+
498
+ #test if lambda function is correctly evaluated
499
+ params = process_metrics(params, metrics)
500
+ assert params["y"] == params["x"]
501
+
502
+ #test if we can do the same with a string
503
+ params = {"x": 15, "b": 12}
504
+ metrics["y"] = "x"
505
+ params = process_metrics(params, metrics)
506
+ assert params["y"] == params["x"]
507
+
508
+ #test if composability works correctly
509
+ params = {"x": 15, "b": 12}
510
+ metrics = OrderedDict()
511
+ metrics["y"] = "x"
512
+ metrics["z"] = "y"
513
+ params = process_metrics(params, metrics)
514
+ assert params["z"] == params["x"]
515
+
516
+ #test ValueError is raised when metrics is not an OrderedDict
517
+ with pytest.raises(ValueError):
518
+ params = process_metrics(params, {})
519
+
520
+ #test ValueError is raised when b already exists in params
521
+ params = {"x": 15, "b": 12}
522
+ metrics = OrderedDict()
523
+ metrics["b"] = "x"
524
+ with pytest.raises(ValueError):
525
+ params = process_metrics(params, metrics)
@@ -1,331 +0,0 @@
1
- """This module contains all CUDA specific kernel_tuner functions"""
2
- from __future__ import print_function
3
-
4
- import logging
5
- import time
6
- import numpy as np
7
-
8
- import cupy as cp
9
-
10
- import cupy.cuda as cuda
11
-
12
- from kernel_tuner.nvml import nvml
13
-
14
-
15
-
16
-
17
- class CupyBackend(object):
18
- """Class that groups the CUDA functions on maintains state about the device"""
19
-
20
- def __init__(self, device=0, iterations=7, compiler_options=None):
21
- """instantiate CudaFunctions object used for interacting with the CUDA device
22
-
23
- Instantiating this object will inspect and store certain device properties at
24
- runtime, which are used during compilation and/or execution of kernels by the
25
- kernel tuner. It also maintains a reference to the most recently compiled
26
- source module for copying data to constant memory before kernel launch.
27
-
28
- :param device: Number of CUDA device to use for this context
29
- :type device: int
30
-
31
- :param iterations: Number of iterations used while benchmarking a kernel, 7 by default.
32
- :type iterations: int
33
- """
34
-
35
- self.allocations = []
36
- self.compiler_options = compiler_options
37
- self.iterations = iterations
38
-
39
- self.dev = cuda.Device(device)
40
- self.dev.use() #make this the current device
41
- self.smem_size = 0
42
- self.use_nvml = False
43
-
44
- self.stream = cuda.Stream()
45
- self.max_threads = self.dev.attributes["MaxThreadsPerBlock"]
46
- self.cc = self.dev.compute_capability
47
-
48
- self.env = {"attributes": self.dev.attributes}
49
- self.name = "cupy does not name device"
50
-
51
-
52
-
53
- def ready_argument_list(self, arguments):
54
- """ready argument list to be passed to the kernel, allocates gpu mem
55
-
56
- :param arguments: List of arguments to be passed to the kernel.
57
- The order should match the argument list on the CUDA kernel.
58
- Allowed values are np.ndarray, and/or np.int32, np.float32, and so on.
59
- :type arguments: list(numpy objects)
60
-
61
- :returns: A list of arguments that can be passed to an CUDA kernel.
62
- :rtype: list( pycuda.driver.DeviceAllocation, np.int32, ... )
63
- """
64
- gpu_args = []
65
- for arg in arguments:
66
- # if arg i is a numpy array copy to device
67
- if isinstance(arg, np.ndarray):
68
- alloc = cp.asarray(arg)
69
- self.allocations.append(alloc)
70
- gpu_args.append(alloc)
71
- else: # if not an array, just pass argument along
72
- #if we remove other checks this might just pass along cupy arrays too
73
- gpu_args.append(arg)
74
- return gpu_args
75
-
76
-
77
- def compile(self, kernel_instance):
78
- """call the CUDA compiler to compile the kernel, return the device function
79
-
80
- :param kernel_name: The name of the kernel to be compiled, used to lookup the
81
- function after compilation.
82
- :type kernel_name: string
83
-
84
- :param kernel_string: The CUDA kernel code that contains the function `kernel_name`
85
- :type kernel_string: string
86
-
87
- :returns: An CUDA kernel that can be called directly.
88
- :rtype: pycuda.driver.Function
89
- """
90
- kernel_string = kernel_instance.kernel_string
91
- kernel_name = kernel_instance.name
92
-
93
- try:
94
- #mimic pycuda behavior for the moment, this can be removed once
95
- #https://github.com/cupy/cupy/pull/3319
96
- #has been merged
97
- no_extern_c = not 'extern "C"' in kernel_string
98
- if no_extern_c:
99
- kernel_string = 'extern "C" {\n' + kernel_string + '\n}\n'
100
-
101
- #self.current_module = cp.RawModule(code=kernel_string, options=self.compiler_options)
102
- self.current_module = cp.RawModule(code=kernel_string)
103
-
104
- func = self.current_module.get_function(kernel_name)
105
- return func
106
- except Exception as e:
107
- if "uses too much shared data" in e.stderr:
108
- raise Exception("uses too much shared data")
109
- else:
110
- raise e
111
-
112
-
113
- def benchmark(self, func, gpu_args, threads, grid):
114
- """runs the kernel and measures time repeatedly, returns average time
115
-
116
- Runs the kernel and measures kernel execution time repeatedly, number of
117
- iterations is set during the creation of CudaFunctions. Benchmark returns
118
- a robust average, from all measurements the fastest and slowest runs are
119
- discarded and the rest is included in the returned average. The reason for
120
- this is to be robust against initialization artifacts and other exceptional
121
- cases.
122
-
123
- :param func: A PyCuda kernel compiled for this specific kernel configuration
124
- :type func: pycuda.driver.Function
125
-
126
- :param gpu_args: A list of arguments to the kernel, order should match the
127
- order in the code. Allowed values are either variables in global memory
128
- or single values passed by value.
129
- :type gpu_args: list( pycuda.driver.DeviceAllocation, np.int32, ...)
130
-
131
- :param threads: A tuple listing the number of threads in each dimension of
132
- the thread block
133
- :type threads: tuple(int, int, int)
134
-
135
- :param grid: A tuple listing the number of thread blocks in each dimension
136
- of the grid
137
- :type grid: tuple(int, int)
138
-
139
- :returns: A dictionary with benchmark results.
140
- :rtype: dict()
141
- """
142
- result = dict()
143
- result["times"] = []
144
- result["power"] = []
145
- energy = []
146
- start = cuda.Event()
147
- end = cuda.Event()
148
- self.dev.synchronize()
149
- for _ in range(self.iterations):
150
- power_readings = []
151
- self.stream.record(start)
152
- self.run_kernel(func, gpu_args, threads, grid, stream=self.stream)
153
- self.stream.record(end)
154
- #measure power usage until kernel is done
155
- t0 = time.time()
156
- while not end.done:
157
- if self.use_nvml:
158
- power_readings.append([time.time()-t0, self.nvml.pwr_usage()])
159
- end.synchronize()
160
- execution_time = cuda.get_elapsed_time(start, end) #ms
161
- result["times"].append(execution_time)
162
-
163
- #pre and postfix to start at 0 and end at kernel end
164
- if power_readings:
165
- power_readings = [[0.0, power_readings[0][1]]] + power_readings
166
- power_readings = power_readings + [[execution_time / 1000.0, power_readings[-1][1]]]
167
- result["power"].append(power_readings) #time in s, power usage in milliwatts
168
-
169
- #compute energy consumption as area under curve
170
- x = [d[0] for d in power_readings]
171
- y = [d[1]/1000.0 for d in power_readings] #convert to watts
172
- energy.append(np.trapz(y,x)) #in Joule
173
-
174
- result["time"] = np.mean(result["times"])
175
- if (self.iterations > 10) and energy:
176
- result["energy"] = np.mean(energy[10:])
177
- return result
178
-
179
- def copy_constant_memory_args(self, cmem_args):
180
- """adds constant memory arguments to the most recently compiled module
181
-
182
- :param cmem_args: A dictionary containing the data to be passed to the
183
- device constant memory. The format to be used is as follows: A
184
- string key is used to name the constant memory symbol to which the
185
- value needs to be copied. Similar to regular arguments, these need
186
- to be numpy objects, such as np.ndarray or np.int32, and so on.
187
- :type cmem_args: dict( string: np.ndarray, ... )
188
- """
189
- logging.debug('copy_constant_memory_args called')
190
- logging.debug('current module: ' + str(self.current_module))
191
- for k, v in cmem_args.items():
192
- #symbol = self.current_module.get_global(k)[0]
193
- logging.debug('copying to symbol: ' + str(symbol))
194
- logging.debug('array to be copied: ')
195
- logging.debug(v.nbytes)
196
- logging.debug(v.dtype)
197
- logging.debug(v.flags)
198
- #drv.memcpy_htod(symbol, v)
199
-
200
- def copy_shared_memory_args(self, smem_args):
201
- """add shared memory arguments to the kernel"""
202
- self.smem_size = smem_args["size"]
203
-
204
- def copy_texture_memory_args(self, texmem_args):
205
- """adds texture memory arguments to the most recently compiled module
206
-
207
- :param texmem_args: A dictionary containing the data to be passed to the
208
- device texture memory. See tune_kernel().
209
- :type texmem_args: dict
210
- """
211
-
212
- """
213
- filter_mode_map = { 'point': drv.filter_mode.POINT,
214
- 'linear': drv.filter_mode.LINEAR }
215
- address_mode_map = { 'border': drv.address_mode.BORDER,
216
- 'clamp': drv.address_mode.CLAMP,
217
- 'mirror': drv.address_mode.MIRROR,
218
- 'wrap': drv.address_mode.WRAP }
219
-
220
- logging.debug('copy_texture_memory_args called')
221
- logging.debug('current module: ' + str(self.current_module))
222
- self.texrefs = []
223
- for k, v in texmem_args.items():
224
- tex = self.current_module.get_texref(k)
225
- self.texrefs.append(tex)
226
-
227
- logging.debug('copying to texture: ' + str(k))
228
- if not isinstance(v, dict):
229
- data = v
230
- else:
231
- data = v['array']
232
- logging.debug('texture to be copied: ')
233
- logging.debug(data.nbytes)
234
- logging.debug(data.dtype)
235
- logging.debug(data.flags)
236
-
237
- drv.matrix_to_texref(data, tex, order="C")
238
-
239
- if isinstance(v, dict):
240
- if 'address_mode' in v and v['address_mode'] is not None:
241
- # address_mode is set per axis
242
- amode = v['address_mode']
243
- if not isinstance(amode, list):
244
- amode = [ amode ] * data.ndim
245
- for i, m in enumerate(amode):
246
- try:
247
- if m is not None:
248
- tex.set_address_mode(i, address_mode_map[m])
249
- except KeyError:
250
- raise ValueError('Unknown address mode: ' + m)
251
- if 'filter_mode' in v and v['filter_mode'] is not None:
252
- fmode = v['filter_mode']
253
- try:
254
- tex.set_filter_mode(filter_mode_map[fmode])
255
- except KeyError:
256
- raise ValueError('Unknown filter mode: ' + fmode)
257
- if 'normalized_coordinates' in v and v['normalized_coordinates']:
258
- tex.set_flags(tex.get_flags() | drv.TRSF_NORMALIZED_COORDINATES)
259
- """
260
- pass
261
-
262
-
263
- def run_kernel(self, func, gpu_args, threads, grid, stream=None):
264
- """runs the CUDA kernel passed as 'func'
265
-
266
- :param func: A PyCuda kernel compiled for this specific kernel configuration
267
- :type func: pycuda.driver.Function
268
-
269
- :param gpu_args: A list of arguments to the kernel, order should match the
270
- order in the code. Allowed values are either variables in global memory
271
- or single values passed by value.
272
- :type gpu_args: list( pycuda.driver.DeviceAllocation, np.int32, ...)
273
-
274
- :param threads: A tuple listing the number of threads in each dimension of
275
- the thread block
276
- :type threads: tuple(int, int, int)
277
-
278
- :param grid: A tuple listing the number of thread blocks in each dimension
279
- of the grid
280
- :type grid: tuple(int, int)
281
- """
282
- func(grid, threads, gpu_args, shared_mem=self.smem_size)
283
-
284
- def memset(self, allocation, value, size):
285
- """set the memory in allocation to the value in value
286
-
287
- :param allocation: A GPU memory allocation unit
288
- :type allocation: pycuda.driver.DeviceAllocation
289
-
290
- :param value: The value to set the memory to
291
- :type value: a single 8-bit unsigned int
292
-
293
- :param size: The size of to the allocation unit in bytes
294
- :type size: int
295
-
296
- """
297
- #drv.memset_d8(allocation, value, size)
298
- allocation = cp.full_like(allocation, value)
299
-
300
-
301
- def memcpy_dtoh(self, dest, src):
302
- """perform a device to host memory copy
303
-
304
- :param dest: A numpy array in host memory to store the data
305
- :type dest: np.ndarray
306
-
307
- :param src: A GPU memory allocation unit
308
- :type src: pycuda.driver.DeviceAllocation
309
- """
310
- if isinstance(src, cp.ndarray):
311
- dest = cp.ndarray.get()
312
- else:
313
- dest = src
314
-
315
- def memcpy_htod(self, dest, src):
316
- """perform a host to device memory copy
317
-
318
- :param dest: A GPU memory allocation unit
319
- :type dest: pycuda.driver.DeviceAllocation
320
-
321
- :param src: A numpy array in host memory to store the data
322
- :type src: np.ndarray
323
- """
324
- if isinstance(dest, cp.ndarray):
325
- cp.copyto(dest, src) #might fail if src is not cupy array
326
- else:
327
- dest = src
328
-
329
- units = {'time': 'ms',
330
- 'power': 's,mW',
331
- 'energy': 'J'}
File without changes
File without changes
File without changes