kernel-tuner 0.3.0__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 (43) hide show
  1. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/PKG-INFO +11 -11
  2. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/README.rst +7 -7
  3. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/c.py +25 -15
  4. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/core.py +8 -4
  5. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/cuda.py +21 -4
  6. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/interface.py +19 -11
  7. kernel_tuner-0.3.2/kernel_tuner/kernelbuilder.py +98 -0
  8. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/opencl.py +3 -3
  9. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/runners/sequential.py +18 -5
  10. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/bayes_opt.py +12 -1
  11. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/minimize.py +14 -5
  12. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/util.py +98 -14
  13. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/wrappers.py +1 -1
  14. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/PKG-INFO +11 -11
  15. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/SOURCES.txt +1 -0
  16. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/setup.py +1 -1
  17. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_c_functions.py +36 -3
  18. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_core.py +1 -1
  19. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_cuda_functions.py +7 -21
  20. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_cuda_mocked.py +6 -4
  21. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_interface.py +10 -10
  22. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_minimize.py +1 -0
  23. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_opencl_functions.py +6 -1
  24. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_util_functions.py +40 -3
  25. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/LICENSE +0 -0
  26. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/MANIFEST.in +0 -0
  27. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/__init__.py +0 -0
  28. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/nvml.py +0 -0
  29. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/runners/__init__.py +0 -0
  30. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/__init__.py +0 -0
  31. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/basinhopping.py +0 -0
  32. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/brute_force.py +0 -0
  33. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/diff_evo.py +0 -0
  34. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/firefly_algorithm.py +0 -0
  35. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/genetic_algorithm.py +0 -0
  36. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/pso.py +0 -0
  37. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/random_sample.py +0 -0
  38. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/simulated_annealing.py +0 -0
  39. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/dependency_links.txt +0 -0
  40. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/requires.txt +0 -0
  41. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/top_level.txt +0 -0
  42. {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/setup.cfg +0 -0
  43. {kernel_tuner-0.3.0 → 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.0
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
205
204
  Provides-Extra: doc
206
- Provides-Extra: cuda_opencl
207
- Provides-Extra: tutorial
208
205
  Provides-Extra: cuda
209
206
  Provides-Extra: opencl
207
+ Provides-Extra: cuda_opencl
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,22 +90,24 @@ 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
- def compile(self, kernel_name, kernel_string):
110
+ def compile(self, kernel_instance):
109
111
  """call the C compiler to compile the kernel, return the function
110
112
 
111
113
  :param kernel_name: The name of the kernel to be compiled, used to lookup the
@@ -118,7 +120,9 @@ class CFunctions(object):
118
120
  :returns: An ctypes function that can be called directly.
119
121
  :rtype: ctypes._FuncPtr
120
122
  """
121
- logging.debug('compiling ' + kernel_name)
123
+ logging.debug('compiling ' + kernel_instance.name)
124
+
125
+ kernel_string = kernel_instance.kernel_string
122
126
 
123
127
  if self.lib != None:
124
128
  self.cleanup_lib()
@@ -134,25 +138,31 @@ class CFunctions(object):
134
138
  else:
135
139
  compiler_options.append("-fopenmp")
136
140
 
137
- # TODO: Get suffix from KernelSources, and adapt the logic below
141
+ #if filename is known, use that one
142
+ suffix = kernel_instance.kernel_source.get_user_suffix()
138
143
 
139
- #select right suffix based on compiler
140
- suffix = ".cc"
144
+ #if code contains device code, suffix .cu is required
145
+ device_code_signals = ["__global", "__syncthreads()", "threadIdx"]
146
+ if any([snippet in kernel_string for snippet in device_code_signals]):
147
+ suffix = ".cu"
141
148
 
142
149
  #detect whether to use nvcc as default instead of g++, may overrule an explicitly passed g++
143
- if ("#include <cuda" in kernel_string) or ("cudaMemcpy" in kernel_string):
150
+ if (suffix == ".cu") or ("#include <cuda" in kernel_string) or ("cudaMemcpy" in kernel_string):
144
151
  if self.compiler == "g++" and self.nvcc_available:
145
152
  self.compiler = "nvcc"
146
153
 
147
- #if contains device code suffix .cu is required by nvcc
148
- if self.compiler == "nvcc" and "__global__" in kernel_string:
149
- suffix = ".cu"
150
- if self.compiler in ["gfortran", "pgfortran", "ftn", "ifort"]:
151
- suffix = ".F90"
154
+ if suffix is None:
155
+ #select right suffix based on compiler
156
+ suffix = ".cc"
157
+
158
+ if self.compiler in ["gfortran", "pgfortran", "ftn", "ifort"]:
159
+ suffix = ".F90"
152
160
 
153
161
  if self.compiler == "nvcc":
154
162
  compiler_options = ["-Xcompiler=" + c for c in compiler_options]
155
163
 
164
+ #this basically checks if we aren't compiling Fortran
165
+ #at the moment any C, C++, or CUDA code is assumed to use extern "C" linkage
156
166
  if ".c" in suffix:
157
167
  if not "extern \"C\"" in kernel_string:
158
168
  kernel_string = "extern \"C\" {\n" + kernel_string + "\n}"
@@ -194,7 +204,7 @@ class CFunctions(object):
194
204
 
195
205
 
196
206
  self.lib = numpy.ctypeslib.load_library(filename, '.')
197
- func = getattr(self.lib, kernel_name)
207
+ func = getattr(self.lib, kernel_instance.name)
198
208
  func.restype = C.c_float
199
209
 
200
210
  finally:
@@ -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
@@ -310,6 +310,9 @@ class DeviceInterface(object):
310
310
  return None
311
311
 
312
312
  #add constant memory arguments to compiled module
313
+ if kernel_options.smem_args is not None:
314
+ self.dev.copy_shared_memory_args(kernel_options.smem_args)
315
+ #add constant memory arguments to compiled module
313
316
  if kernel_options.cmem_args is not None:
314
317
  self.dev.copy_constant_memory_args(kernel_options.cmem_args)
315
318
  #add texture memory arguments to compiled module
@@ -341,12 +344,13 @@ class DeviceInterface(object):
341
344
  #compile kernel_string into device func
342
345
  func = None
343
346
  try:
344
- func = self.dev.compile(instance.name, instance.kernel_string)
347
+ func = self.dev.compile(instance)
345
348
  except Exception as e:
346
349
  #compiles may fail because certain kernel configurations use too
347
350
  #much shared memory for example, the desired behavior is to simply
348
351
  #skip over this configuration and try the next one
349
- 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):
350
354
  logging.debug('compile_kernel failed due to kernel using too much shared memory')
351
355
  if verbose:
352
356
  print("skipping config", instance.name, "reason: too much shared memory used")
@@ -47,7 +47,13 @@ class CudaFunctions(object):
47
47
 
48
48
  drv.init()
49
49
  self.context = drv.Device(device).make_context()
50
- self.nvml = nvml(device)
50
+
51
+ try:
52
+ self.nvml = nvml(device)
53
+ self.nvml.pwr_usage()
54
+ self.use_nvml = True
55
+ except:
56
+ self.use_nvml = False
51
57
 
52
58
  #inspect device properties
53
59
  devprops = {str(k): v for (k, v) in self.context.get_device().get_attributes().items()}
@@ -71,6 +77,9 @@ class CudaFunctions(object):
71
77
  #create a stream
72
78
  self.stream = drv.Stream()
73
79
 
80
+ #default dynamically allocated shared memory size, can be overwritten using smem_args
81
+ self.smem_size = 0
82
+
74
83
  #collect environment information
75
84
  env = dict()
76
85
  env["device_name"] = self.context.get_device().name()
@@ -113,7 +122,7 @@ class CudaFunctions(object):
113
122
  return gpu_args
114
123
 
115
124
 
116
- def compile(self, kernel_name, kernel_string):
125
+ def compile(self, kernel_instance):
117
126
  """call the CUDA compiler to compile the kernel, return the device function
118
127
 
119
128
  :param kernel_name: The name of the kernel to be compiled, used to lookup the
@@ -126,6 +135,9 @@ class CudaFunctions(object):
126
135
  :returns: An CUDA kernel that can be called directly.
127
136
  :rtype: pycuda.driver.Function
128
137
  """
138
+ kernel_string = kernel_instance.kernel_string
139
+ kernel_name = kernel_instance.name
140
+
129
141
  try:
130
142
  no_extern_c = 'extern "C"' in kernel_string
131
143
 
@@ -191,7 +203,8 @@ class CudaFunctions(object):
191
203
  #measure power usage until kernel is done
192
204
  t0 = time.time()
193
205
  while not end.query():
194
- power_readings.append([time.time()-t0, self.nvml.pwr_usage()])
206
+ if self.use_nvml:
207
+ power_readings.append([time.time()-t0, self.nvml.pwr_usage()])
195
208
  end.synchronize()
196
209
  execution_time = end.time_since(start) #ms
197
210
  result["times"].append(execution_time)
@@ -233,6 +246,10 @@ class CudaFunctions(object):
233
246
  logging.debug(v.flags)
234
247
  drv.memcpy_htod(symbol, v)
235
248
 
249
+ def copy_shared_memory_args(self, smem_args):
250
+ """add shared memory arguments to the kernel"""
251
+ self.smem_size = smem_args["size"]
252
+
236
253
  def copy_texture_memory_args(self, texmem_args):
237
254
  """adds texture memory arguments to the most recently compiled module
238
255
 
@@ -308,7 +325,7 @@ class CudaFunctions(object):
308
325
  of the grid
309
326
  :type grid: tuple(int, int)
310
327
  """
311
- func(*gpu_args, block=threads, grid=grid, stream=stream, texrefs=self.texrefs)
328
+ func(*gpu_args, block=threads, grid=grid, stream=stream, shared=self.smem_size, texrefs=self.texrefs)
312
329
 
313
330
  def memset(self, allocation, value, size):
314
331
  """set the memory in allocation to the value in value
@@ -80,13 +80,12 @@ _kernel_options = Options([
80
80
  parameters. This function should take one positional argument,
81
81
  which will be used to pass a dict containing the parameters.
82
82
  The function should return a string with the source code for
83
- the kernel.""",
84
- "string or list and/or callable")),
85
- ("lang", ("""Specifies the language used for GPU kernels. The kernel_tuner
83
+ the kernel.""", "string or list and/or callable")),
84
+ ("lang", ("""Specifies the language used for GPU kernels. The kernel_tuner
86
85
  automatically detects the language, but if it fails, you may specify
87
86
  the language using this argument, currently supported: "CUDA",
88
87
  "OpenCL", or "C".""", "string")),
89
- ("problem_size", ("""An int or string, or 1,2,3-dimensional tuple
88
+ ("problem_size", ("""An int or string, or 1,2,3-dimensional tuple
90
89
  containing the size from which the grid dimensions of the kernel
91
90
  will be computed.
92
91
 
@@ -129,6 +128,12 @@ _kernel_options = Options([
129
128
  the grid dimensions in the z-direction, ["block_size_z"] by default.
130
129
  If you do not want to divide the problem_size, you should pass an empty list.
131
130
  See grid_div_x for more details.""", "list")),
131
+ ("smem_args", ("""CUDA-specific feature for specifying shared memory options
132
+ to the kernel. At the moment only 'size' is supported, but setting the
133
+ shared memory configuration on Kepler GPUs for example could be added
134
+ in the future. Size should denote the number of bytes for to use when
135
+ dynamically allocating shared memory.""",
136
+ "dict(string: numpy object)")),
132
137
  ("cmem_args", ("""CUDA-specific feature for specifying constant memory
133
138
  arguments to the kernel. In OpenCL these are handled as normal
134
139
  kernel arguments, but in CUDA you can copy to a symbol. The way you
@@ -142,7 +147,8 @@ _kernel_options = Options([
142
147
  the texture contents. These contents can be either simply a numpy object,
143
148
  or a dictionary containing the numpy object under the key 'array' plus the
144
149
  configuration options 'filter_mode' ('point' or 'linear), 'address_mode'
145
- (a list of 'border', 'clamp', 'mirror', 'wrap' per axis), 'normalized_coordinates' (True/False).""",
150
+ (a list of 'border', 'clamp', 'mirror', 'wrap' per axis),
151
+ 'normalized_coordinates' (True/False).""",
146
152
  "dict(string: numpy object or dict)")),
147
153
  ("block_size_names", ("""A list of strings that replace the defaults for the names
148
154
  that denote the thread block dimensions. If not passed, the behavior
@@ -309,10 +315,11 @@ _tuning_options = Options([
309
315
  * too many resources requested for launch
310
316
 
311
317
  verbose is False by default.""", "bool")),
312
- ("cache",("""filename for caching/logging benchmarked instances
318
+ ("cache", ("""filename for caching/logging benchmarked instances
313
319
  filename uses suffix ".json"
314
320
  if the file exists it is read and tuning continues from this file
315
- """, "string"))
321
+ """, "string")),
322
+ ("metrics", ("specifies user-defined metrics", "OrderedDict"))
316
323
  ])
317
324
 
318
325
  _device_options = Options([
@@ -357,10 +364,10 @@ _tune_kernel_docstring = """ Tune a CUDA kernel given a set of tunable parameter
357
364
  def tune_kernel(kernel_name, kernel_string, problem_size, arguments,
358
365
  tune_params, grid_div_x=None, grid_div_y=None, grid_div_z=None,
359
366
  restrictions=None, answer=None, atol=1e-6, verify=None, verbose=False,
360
- lang=None, device=0, platform=0, cmem_args=None, texmem_args=None,
367
+ lang=None, device=0, platform=0, smem_args=None, cmem_args=None, texmem_args=None,
361
368
  compiler=None, compiler_options=None, log=None,
362
369
  iterations=7, block_size_names=None, quiet=False, strategy=None, strategy_options=None,
363
- cache=None):
370
+ cache=None, metrics=None):
364
371
 
365
372
  if log:
366
373
  logging.basicConfig(filename=kernel_name + datetime.now().strftime('%Y%m%d-%H:%M:%S') + '.log', level=log)
@@ -444,7 +451,8 @@ def tune_kernel(kernel_name, kernel_string, problem_size, arguments,
444
451
  if results: #checks if results is not empty
445
452
  best_config = min(results, key=lambda x: x['time'])
446
453
  units = getattr(runner, "units", None)
447
- 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)
448
456
  else:
449
457
  print("no results to report")
450
458
 
@@ -490,7 +498,7 @@ _run_kernel_docstring = """Compile and run a single kernel
490
498
 
491
499
  def run_kernel(kernel_name, kernel_string, problem_size, arguments,
492
500
  params, grid_div_x=None, grid_div_y=None, grid_div_z=None,
493
- lang=None, device=0, platform=0, cmem_args=None, texmem_args=None, compiler=None, compiler_options=None,
501
+ lang=None, device=0, platform=0, smem_args=None, cmem_args=None, texmem_args=None, compiler=None, compiler_options=None,
494
502
  block_size_names=None, quiet=False, log=None):
495
503
 
496
504
  if log:
@@ -0,0 +1,98 @@
1
+ import numpy as np
2
+
3
+ from kernel_tuner import core
4
+ from kernel_tuner.interface import Options, _kernel_options
5
+
6
+ class PythonKernel(object):
7
+
8
+ def __init__(self, kernel_name, kernel_string, problem_size, arguments, params, inputs=None, outputs=None, device=0, platform=0,
9
+ block_size_names=None, grid_div_x=None, grid_div_y=None, grid_div_z=None, verbose=True, lang=None):
10
+ """ Construct Python helper object to compile and call the kernel from Python
11
+
12
+ This object compiles a GPU kernel parameterized using the parameters in params.
13
+ GPU memory is allocated for each argument using its size and type as listed in arguments.
14
+ The object can be called directly as a function with the kernel arguments as function arguments.
15
+ Kernel arguments marked as inputs will be copied to the GPU on every kernel launch.
16
+ Only the kernel arguments marked as outputs will be returned, note that the result is always
17
+ returned in a list, even when there is only one output.
18
+
19
+ Most of the arguments to this function are the same as with tune_kernel or run_kernel in Kernel Tuner,
20
+ and are therefore not duplicated here. The two new arguments are:
21
+
22
+ :param inputs: a boolean list of length arguments to signal whether an argument is input to the kernel
23
+ :type inputs: list(bool)
24
+
25
+ :param outputs: a boolean list of length arguments to signal whether an argument is output of the kernel
26
+ :type outputs: list(bool)
27
+
28
+ """
29
+ #construct device interface
30
+ kernel_source = core.KernelSource(kernel_string, lang)
31
+ self.dev = core.DeviceInterface(kernel_source, device=device)
32
+
33
+ #construct kernel_options to hold information about the kernel
34
+ opts = locals()
35
+ kernel_options = Options([(k, opts[k]) for k in _kernel_options.keys() if k in opts.keys()])
36
+
37
+ #instantiate the kernel given the parameters in params
38
+ self.kernel_instance = self.dev.create_kernel_instance(kernel_source, kernel_options, params, verbose)
39
+
40
+ #compile the kernel
41
+ self.func = self.dev.compile_kernel(self.kernel_instance, verbose)
42
+
43
+ #setup GPU memory
44
+ self.gpu_args = self.dev.ready_argument_list(arguments)
45
+ if inputs:
46
+ self.inputs = inputs
47
+ else:
48
+ self.inputs = [True for _ in arguments]
49
+ if outputs:
50
+ self.outputs = outputs
51
+ else:
52
+ self.outputs = [True for _ in arguments]
53
+
54
+ def update_gpu_args(self, args):
55
+ for i, arg in enumerate(args):
56
+ if self.inputs[i]:
57
+ if isinstance(args[i], np.ndarray):
58
+ self.dev.dev.memcpy_htod(self.gpu_args[i], arg)
59
+ else:
60
+ self.gpu_args[i] = arg
61
+ return self.gpu_args
62
+
63
+ def get_gpu_result(self, args):
64
+ results = []
65
+ for i, _ in enumerate(self.gpu_args):
66
+ if self.outputs[i] and isinstance(args[i], np.ndarray):
67
+ res = np.zeros_like(args[i])
68
+ self.dev.memcpy_dtoh(res, self.gpu_args[i])
69
+ results.append(res)
70
+ return results
71
+
72
+ def run_kernel(self, args):
73
+ """Run the GPU kernel
74
+
75
+ Copy the arguments marked as inputs to the GPU
76
+ Call the GPU kernel
77
+ Copy the arguments marked as outputs from the GPU
78
+ Return the outputs in a list of numpy arrays
79
+
80
+ :param args: A list with the kernel arguments as numpy arrays or numpy scalars
81
+ :type args: list(np.ndarray or np.generic)
82
+ """
83
+ self.update_gpu_args(args)
84
+ self.dev.run_kernel(self.func, self.gpu_args, self.kernel_instance)
85
+ return self.get_gpu_result(args)
86
+
87
+ def __call__(self, *args):
88
+ """Run the GPU kernel
89
+
90
+ Copy the arguments marked as inputs to the GPU
91
+ Call the GPU kernel
92
+ Copy the arguments marked as outputs from the GPU
93
+ Return the outputs in a list of numpy arrays
94
+
95
+ :param *args: A variable number of kernel arguments as numpy arrays or numpy scalars
96
+ :type *args: np.ndarray or np.generic
97
+ """
98
+ return self.run_kernel(args)
@@ -69,7 +69,7 @@ class OpenCLFunctions(object):
69
69
  gpu_args.append(arg)
70
70
  return gpu_args
71
71
 
72
- def compile(self, kernel_name, kernel_string):
72
+ def compile(self, kernel_instance):
73
73
  """call the OpenCL compiler to compile the kernel, return the device function
74
74
 
75
75
  :param kernel_name: The name of the kernel to be compiled, used to lookup the
@@ -82,8 +82,8 @@ class OpenCLFunctions(object):
82
82
  :returns: An OpenCL kernel that can be called directly.
83
83
  :rtype: pyopencl.Kernel
84
84
  """
85
- prg = cl.Program(self.ctx, kernel_string).build(options=self.compiler_options)
86
- func = getattr(prg, kernel_name)
85
+ prg = cl.Program(self.ctx, kernel_instance.kernel_string).build(options=self.compiler_options)
86
+ func = getattr(prg, kernel_instance.name)
87
87
  return func
88
88
 
89
89
  def benchmark(self, func, gpu_args, threads, grid):
@@ -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
+
@@ -2,7 +2,15 @@
2
2
  from __future__ import print_function
3
3
 
4
4
  from collections import OrderedDict
5
- from bayes_opt import BayesianOptimization
5
+
6
+ try:
7
+ from bayes_opt import BayesianOptimization
8
+ bayes_opt_present=True
9
+ except Exception:
10
+ BayesianOptimization = None
11
+ bayes_opt_present=False
12
+
13
+
6
14
 
7
15
  from kernel_tuner.strategies import minimize
8
16
 
@@ -47,6 +55,9 @@ def tune(runner, kernel_options, device_options, tuning_options):
47
55
  # acq="ei", xi=1e-1
48
56
  # acq="ucb", kappa=10
49
57
 
58
+ if not bayes_opt_present:
59
+ raise ImportError("Error: optional dependency Bayesian Optimization not installed")
60
+
50
61
  #defaults as used by Bayesian Optimization Python package
51
62
  acq = tuning_options.strategy_options.get("method", "poi")
52
63
  kappa = tuning_options.strategy_options.get("kappa", 2.576)
@@ -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)