kernel-tuner 0.3.1__tar.gz → 0.4.0__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 (55) hide show
  1. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/PKG-INFO +13 -13
  2. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/README.rst +7 -7
  3. kernel_tuner-0.4.0/kernel_tuner/__init__.py +5 -0
  4. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/c.py +24 -12
  5. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/core.py +167 -45
  6. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/cuda.py +54 -45
  7. kernel_tuner-0.4.0/kernel_tuner/cupy.py +303 -0
  8. kernel_tuner-0.4.0/kernel_tuner/integration.py +409 -0
  9. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/interface.py +181 -172
  10. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/kernelbuilder.py +19 -4
  11. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/nvml.py +115 -9
  12. kernel_tuner-0.4.0/kernel_tuner/observers.py +78 -0
  13. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/opencl.py +50 -12
  14. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/runners/sequential.py +24 -12
  15. kernel_tuner-0.4.0/kernel_tuner/runners/simulation.py +244 -0
  16. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/basinhopping.py +4 -2
  17. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/brute_force.py +5 -3
  18. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/diff_evo.py +5 -3
  19. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/firefly_algorithm.py +4 -3
  20. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/genetic_algorithm.py +40 -30
  21. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/minimize.py +41 -31
  22. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/random_sample.py +6 -8
  23. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/simulated_annealing.py +8 -6
  24. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/util.py +285 -96
  25. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/wrappers.py +2 -2
  26. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner.egg-info/PKG-INFO +13 -13
  27. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner.egg-info/SOURCES.txt +7 -1
  28. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner.egg-info/requires.txt +1 -0
  29. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/setup.py +5 -4
  30. kernel_tuner-0.4.0/test/test_c_functions.py +282 -0
  31. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/test/test_core.py +72 -29
  32. kernel_tuner-0.4.0/test/test_cuda_functions.py +64 -0
  33. kernel_tuner-0.4.0/test/test_cuda_mocked.py +144 -0
  34. kernel_tuner-0.4.0/test/test_integration.py +167 -0
  35. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/test/test_interface.py +30 -24
  36. kernel_tuner-0.4.0/test/test_kernelbuilder.py +57 -0
  37. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/test/test_minimize.py +4 -7
  38. kernel_tuner-0.4.0/test/test_observers.py +36 -0
  39. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/test/test_opencl_functions.py +16 -15
  40. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/test/test_runners.py +39 -42
  41. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/test/test_util_functions.py +156 -74
  42. kernel_tuner-0.3.1/kernel_tuner/__init__.py +0 -3
  43. kernel_tuner-0.3.1/kernel_tuner/cupy_backend.py +0 -331
  44. kernel_tuner-0.3.1/test/test_c_functions.py +0 -218
  45. kernel_tuner-0.3.1/test/test_cuda_functions.py +0 -81
  46. kernel_tuner-0.3.1/test/test_cuda_mocked.py +0 -139
  47. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/LICENSE +0 -0
  48. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/MANIFEST.in +0 -0
  49. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/runners/__init__.py +0 -0
  50. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/__init__.py +0 -0
  51. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/bayes_opt.py +0 -0
  52. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner/strategies/pso.py +0 -0
  53. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner.egg-info/dependency_links.txt +0 -0
  54. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/kernel_tuner.egg-info/top_level.txt +0 -0
  55. {kernel_tuner-0.3.1 → kernel_tuner-0.4.0}/setup.cfg +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.4.0
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
@@ -194,16 +194,16 @@ Classifier: Intended Audience :: Education
194
194
  Classifier: License :: OSI Approved :: Apache Software License
195
195
  Classifier: Natural Language :: English
196
196
  Classifier: Operating System :: POSIX :: Linux
197
- Classifier: Programming Language :: Python :: 3.5
198
197
  Classifier: Programming Language :: Python :: 3.6
199
198
  Classifier: Programming Language :: Python :: 3.7
199
+ Classifier: Programming Language :: Python :: 3.8
200
200
  Classifier: Topic :: Scientific/Engineering
201
201
  Classifier: Topic :: Software Development
202
202
  Classifier: Topic :: System :: Distributed Computing
203
- Classifier: Development Status :: 4 - Beta
204
- Provides-Extra: dev
203
+ Classifier: Development Status :: 5 - Production/Stable
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
@@ -0,0 +1,5 @@
1
+
2
+ from kernel_tuner.integration import store_results, create_device_targets
3
+
4
+ from kernel_tuner.interface import tune_kernel, run_kernel
5
+
@@ -73,6 +73,12 @@ class CFunctions(object):
73
73
  self.env = env
74
74
  self.name = platform.processor()
75
75
 
76
+ def __enter__(self):
77
+ return self
78
+
79
+ def __exit__(self, *exc):
80
+ pass
81
+
76
82
  def ready_argument_list(self, arguments):
77
83
  """ready argument list to be passed to the C function
78
84
 
@@ -90,30 +96,29 @@ class CFunctions(object):
90
96
  if not isinstance(arg, (numpy.ndarray, numpy.number)):
91
97
  raise TypeError("Argument is not numpy ndarray or numpy scalar %s" % type(arg))
92
98
  dtype_str = str(arg.dtype)
93
- data = arg.copy()
99
+ #data = arg.copy()
94
100
  if isinstance(arg, numpy.ndarray):
95
101
  if dtype_str in dtype_map.keys():
96
102
  # In numpy <= 1.15, ndarray.ctypes.data_as does not itself keep a reference
97
103
  # to its underlying array, so we need to store a reference to arg.copy()
98
104
  # in the Argument object manually to avoid it being deleted.
99
105
  # (This changed in numpy > 1.15.)
100
- data_ctypes = data.ctypes.data_as(C.POINTER(dtype_map[dtype_str]))
106
+ #data_ctypes = data.ctypes.data_as(C.POINTER(dtype_map[dtype_str]))
107
+ data_ctypes = arg.ctypes.data_as(C.POINTER(dtype_map[dtype_str]))
101
108
  else:
102
109
  raise TypeError("unknown dtype for ndarray")
103
110
  elif isinstance(arg, numpy.generic):
104
111
  data_ctypes = dtype_map[dtype_str](arg)
105
- ctype_args[i] = Argument(numpy=data, ctypes=data_ctypes)
112
+ #ctype_args[i] = Argument(numpy=data, ctypes=data_ctypes)
113
+ ctype_args[i] = Argument(numpy=arg, ctypes=data_ctypes)
106
114
  return ctype_args
107
115
 
108
116
  def compile(self, kernel_instance):
109
117
  """call the C compiler to compile the kernel, return the function
110
118
 
111
- :param kernel_name: The name of the kernel to be compiled, used to lookup the
112
- function after compilation.
113
- :type kernel_name: string
114
-
115
- :param kernel_string: The C code that contains the function `kernel_name`
116
- :type kernel_string: string
119
+ :param kernel_instance: An object representing the specific instance of the tunable kernel
120
+ in the parameter space.
121
+ :type kernel_instance: kernel_tuner.core.KernelInstance
117
122
 
118
123
  :returns: An ctypes function that can be called directly.
119
124
  :rtype: ctypes._FuncPtr
@@ -121,6 +126,7 @@ class CFunctions(object):
121
126
  logging.debug('compiling ' + kernel_instance.name)
122
127
 
123
128
  kernel_string = kernel_instance.kernel_string
129
+ kernel_name = kernel_instance.name
124
130
 
125
131
  if self.lib != None:
126
132
  self.cleanup_lib()
@@ -134,7 +140,10 @@ class CFunctions(object):
134
140
  if self.compiler == "pgfortran":
135
141
  compiler_options.append("-mp")
136
142
  else:
137
- compiler_options.append("-fopenmp")
143
+ if "#pragma acc" in kernel_string or "!$acc" in kernel_string:
144
+ compiler_options.append("-fopenacc")
145
+ else:
146
+ compiler_options.append("-fopenmp")
138
147
 
139
148
  #if filename is known, use that one
140
149
  suffix = kernel_instance.kernel_source.get_user_suffix()
@@ -189,6 +198,10 @@ class CFunctions(object):
189
198
  kernel_name = match.group(1) + "_mp_" + kernel_name + "_"
190
199
  elif self.compiler == "pgfortran":
191
200
  kernel_name = match.group(1) + "_" + kernel_name + "_"
201
+ else:
202
+ #for functions outside of modules
203
+ if self.compiler in ["gfortran", "ftn", "ifort", "pgfortran"]:
204
+ kernel_name = kernel_name + "_"
192
205
 
193
206
  try:
194
207
  write_file(source_file, kernel_string)
@@ -200,9 +213,8 @@ class CFunctions(object):
200
213
  subprocess.check_call([self.compiler, "-c", source_file] + compiler_options + ["-o", filename + ".o"])
201
214
  subprocess.check_call([self.compiler, filename + ".o"] + compiler_options + ["-shared", "-o", filename + lib_extension] + lib_args)
202
215
 
203
-
204
216
  self.lib = numpy.ctypeslib.load_library(filename, '.')
205
- func = getattr(self.lib, kernel_instance.name)
217
+ func = getattr(self.lib, kernel_name)
206
218
  func.restype = C.c_float
207
219
 
208
220
  finally:
@@ -1,18 +1,27 @@
1
1
  """ Module for grouping the core functionality needed by most runners """
2
2
  from __future__ import print_function
3
3
 
4
+ import re
4
5
  from collections import namedtuple
5
6
  import resource
6
7
  import logging
7
- import numpy
8
+ import numpy as np
8
9
 
10
+ try:
11
+ import cupy as cp
12
+ except ImportError:
13
+ cp = np
14
+
15
+ from kernel_tuner.cupy import CupyFunctions
9
16
  from kernel_tuner.cuda import CudaFunctions
10
17
  from kernel_tuner.opencl import OpenCLFunctions
11
18
  from kernel_tuner.c import CFunctions
19
+ from kernel_tuner.nvml import NVMLObserver
12
20
  import kernel_tuner.util as util
13
21
 
14
22
  _KernelInstance = namedtuple("_KernelInstance", ["name", "kernel_source", "kernel_string", "temp_files", "threads", "grid", "params", "arguments"])
15
23
 
24
+
16
25
  class KernelInstance(_KernelInstance):
17
26
  """Class that represents the specific parameterized instance of a kernel"""
18
27
 
@@ -40,10 +49,11 @@ class KernelSource(object):
40
49
  must be filenames.
41
50
  """
42
51
 
43
- def __init__(self, kernel_sources, lang):
52
+ def __init__(self, kernel_name, kernel_sources, lang):
44
53
  if not isinstance(kernel_sources, list):
45
54
  kernel_sources = [kernel_sources]
46
55
  self.kernel_sources = kernel_sources
56
+ self.kernel_name = kernel_name
47
57
  if lang is None:
48
58
  if callable(self.kernel_sources[0]):
49
59
  raise TypeError("Please specify language when using a code generator function")
@@ -74,7 +84,6 @@ class KernelSource(object):
74
84
  kernel_source = self.kernel_sources[index]
75
85
  return util.get_kernel_string(kernel_source, params)
76
86
 
77
-
78
87
  def prepare_list_of_files(self, kernel_name, params, grid, threads, block_size_names):
79
88
  """ prepare the kernel string along with any additional files
80
89
 
@@ -115,7 +124,7 @@ class KernelSource(object):
115
124
 
116
125
  ks = self.get_kernel_string(i, params)
117
126
  # add preprocessor statements
118
- n, ks = util.prepare_kernel_string(kernel_name, ks, params, grid, threads, block_size_names)
127
+ n, ks = util.prepare_kernel_string(kernel_name, ks, params, grid, threads, block_size_names, self.lang)
119
128
 
120
129
  if i == 0:
121
130
  # primary kernel source
@@ -134,7 +143,6 @@ class KernelSource(object):
134
143
 
135
144
  return name, kernel_string, temp_files
136
145
 
137
-
138
146
  def get_user_suffix(self, index=0):
139
147
  """ Get the suffix of the kernel filename, if the user specified one. Return None otherwise.
140
148
  """
@@ -154,7 +162,11 @@ class KernelSource(object):
154
162
  if suffix is not None:
155
163
  return suffix
156
164
 
157
- _suffixes = {'CUDA': '.cu', 'OpenCL': '.cl', 'C': '.c'}
165
+ _suffixes = {
166
+ 'CUDA': '.cu',
167
+ 'OpenCL': '.cl',
168
+ 'C': '.c'
169
+ }
158
170
  try:
159
171
  return _suffixes[self.lang]
160
172
  except KeyError:
@@ -175,7 +187,7 @@ class KernelSource(object):
175
187
  class DeviceInterface(object):
176
188
  """Class that offers a High-Level Device Interface to the rest of the Kernel Tuner"""
177
189
 
178
- def __init__(self, kernel_source, device=0, platform=0, lang=None, quiet=False, compiler=None, compiler_options=None, iterations=7):
190
+ def __init__(self, kernel_source, device=0, platform=0, quiet=False, compiler=None, compiler_options=None, iterations=7, observers=None):
179
191
  """ Instantiate the DeviceInterface, based on language in kernel source
180
192
 
181
193
  :param kernel_source The kernel sources
@@ -210,13 +222,24 @@ class DeviceInterface(object):
210
222
  logging.debug('DeviceInterface instantiated, lang=%s', lang)
211
223
 
212
224
  if lang == "CUDA":
213
- dev = CudaFunctions(device, compiler_options=compiler_options, iterations=iterations)
225
+ dev = CudaFunctions(device, compiler_options=compiler_options, iterations=iterations, observers=observers)
226
+ elif lang.upper() == "CUPY":
227
+ dev = CupyFunctions(device, compiler_options=compiler_options, iterations=iterations, observers=observers)
214
228
  elif lang == "OpenCL":
215
- dev = OpenCLFunctions(device, platform, compiler_options=compiler_options, iterations=iterations)
229
+ dev = OpenCLFunctions(device, platform, compiler_options=compiler_options, iterations=iterations, observers=observers)
216
230
  elif lang == "C":
217
231
  dev = CFunctions(compiler=compiler, compiler_options=compiler_options, iterations=iterations)
218
232
  else:
219
233
  raise Exception("Sorry, support for languages other than CUDA, OpenCL, or C is not implemented yet")
234
+
235
+ #look for NVMLObserver in observers, if present, enable special tunable parameters through nvml
236
+ self.use_nvml = False
237
+ if observers:
238
+ for obs in observers:
239
+ if isinstance(obs, NVMLObserver):
240
+ self.nvml = obs.nvml
241
+ self.use_nvml = True
242
+
220
243
  self.lang = lang
221
244
  self.dev = dev
222
245
  self.units = dev.units
@@ -224,12 +247,29 @@ class DeviceInterface(object):
224
247
  if not quiet:
225
248
  print("Using: " + self.dev.name)
226
249
 
250
+ dev.__enter__()
251
+
252
+ def __enter__(self):
253
+ return self
254
+
227
255
  def benchmark(self, func, gpu_args, instance, verbose):
228
256
  """benchmark the kernel instance"""
229
257
  logging.debug('benchmark ' + instance.name)
230
258
  logging.debug('thread block dimensions x,y,z=%d,%d,%d', *instance.threads)
231
259
  logging.debug('grid dimensions x,y,z=%d,%d,%d', *instance.grid)
232
260
 
261
+ if self.use_nvml:
262
+ if "nvml_pwr_limit" in instance.params:
263
+ new_limit = int(instance.params["nvml_pwr_limit"] * 1000) #user specifies in Watt, but nvml uses milliWatt
264
+ if self.nvml.pwr_limit != new_limit:
265
+ self.nvml.pwr_limit = new_limit
266
+ if "nvml_gr_clock" in instance.params:
267
+ self.nvml.gr_clock = instance.params["nvml_gr_clock"]
268
+ if "nvml_sm_clock" in instance.params:
269
+ self.nvml.sm_clock = instance.params["nvml_sm_clock"]
270
+ if "nvml_mem_clock" in instance.params:
271
+ self.nvml.mem_clock = instance.params["nvml_mem_clock"]
272
+
233
273
  result = None
234
274
  try:
235
275
  result = self.dev.benchmark(func, gpu_args, instance.threads, instance.grid)
@@ -260,20 +300,19 @@ class DeviceInterface(object):
260
300
  #re-copy original contents of output arguments to GPU memory, to overwrite any changes
261
301
  #by earlier kernel runs
262
302
  for i, arg in enumerate(instance.arguments):
263
- if verify or answer[i] is not None:
264
- if isinstance(arg, numpy.ndarray):
265
- self.dev.memcpy_htod(gpu_args[i], arg)
303
+ if verify or answer[i] is not None and isinstance(arg, (np.ndarray, cp.ndarray)):
304
+ self.dev.memcpy_htod(gpu_args[i], arg)
266
305
 
267
306
  #run the kernel
268
307
  check = self.run_kernel(func, gpu_args, instance)
269
308
  if not check:
270
- return True #runtime failure occured that should be ignored, skip correctness check
309
+ return True #runtime failure occured that should be ignored, skip correctness check
271
310
 
272
311
  #retrieve gpu results to host memory
273
312
  result_host = []
274
313
  for i, arg in enumerate(instance.arguments):
275
- if (verify or answer[i] is not None) and isinstance(arg, numpy.ndarray):
276
- result_host.append(numpy.zeros_like(arg))
314
+ if (verify or answer[i] is not None) and isinstance(arg, (np.ndarray, cp.ndarray)):
315
+ result_host.append(np.zeros_like(arg))
277
316
  self.dev.memcpy_dtoh(result_host[-1], gpu_args[i])
278
317
  else:
279
318
  result_host.append(None)
@@ -294,7 +333,7 @@ class DeviceInterface(object):
294
333
  instance_string = util.get_instance_string(params)
295
334
 
296
335
  logging.debug('compile_and_benchmark ' + instance_string)
297
- mem_usage = round(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss/1024.0, 1)
336
+ mem_usage = round(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024.0, 1)
298
337
  logging.debug('Memory usage : %2.2f MB', mem_usage)
299
338
 
300
339
  verbose = tuning_options.verbose
@@ -349,7 +388,8 @@ class DeviceInterface(object):
349
388
  #compiles may fail because certain kernel configurations use too
350
389
  #much shared memory for example, the desired behavior is to simply
351
390
  #skip over this configuration and try the next one
352
- if "uses too much shared data" in str(e):
391
+ shared_mem_error_messages = ["uses too much shared data", "local memory limit exceeded"]
392
+ if any(msg in str(e) for msg in shared_mem_error_messages):
353
393
  logging.debug('compile_kernel failed due to kernel using too much shared memory')
354
394
  if verbose:
355
395
  print("skipping config", instance.name, "reason: too much shared memory used")
@@ -384,13 +424,18 @@ class DeviceInterface(object):
384
424
 
385
425
  #setup thread block and grid dimensions
386
426
  threads, grid = util.setup_block_and_grid(kernel_options.problem_size, grid_div, params, kernel_options.block_size_names)
387
- if numpy.prod(threads) > self.dev.max_threads:
427
+ if np.prod(threads) > self.dev.max_threads:
388
428
  if verbose:
389
429
  print("skipping config", instance_string, "reason: too many threads per block")
390
430
  return None
391
431
 
392
432
  #obtain the kernel_string and prepare additional files, if any
393
- name, kernel_string, temp_files = kernel_source.prepare_list_of_files(kernel_options.kernel_name, params, grid, threads, kernel_options.block_size_names)
433
+ name, kernel_string, temp_files = kernel_source.prepare_list_of_files(kernel_options.kernel_name, params, grid, threads,
434
+ kernel_options.block_size_names)
435
+
436
+ #check for templated kernel
437
+ if kernel_source.lang == "CUDA" and "<" in name and ">" in name:
438
+ kernel_string, name = wrap_templated_kernel(kernel_string, name)
394
439
 
395
440
  #collect everything we know about this instance and return it
396
441
  return KernelInstance(name, kernel_source, kernel_string, temp_files, threads, grid, params, kernel_options.arguments)
@@ -424,45 +469,37 @@ class DeviceInterface(object):
424
469
  raise e
425
470
  return True
426
471
 
427
-
428
- def __del__(self):
472
+ def __exit__(self, *exc):
429
473
  if hasattr(self, 'dev'):
430
- del self.dev
431
-
474
+ self.dev.__exit__(*exc)
432
475
 
433
476
 
434
477
  def _default_verify_function(instance, answer, result_host, atol, verbose):
435
- """default verify function based on numpy.allclose"""
478
+ """default verify function based on np.allclose"""
436
479
 
437
480
  #first check if the length is the same
438
481
  if len(instance.arguments) != len(answer):
439
482
  raise TypeError("The length of argument list and provided results do not match.")
440
483
  #for each element in the argument list, check if the types match
441
484
  for i, arg in enumerate(instance.arguments):
442
- if answer[i] is not None: #skip None elements in the answer list
443
- if isinstance(answer[i], numpy.ndarray) and isinstance(arg, numpy.ndarray):
485
+ if answer[i] is not None: #skip None elements in the answer list
486
+ if isinstance(answer[i], (np.ndarray, cp.ndarray)) and isinstance(arg, (np.ndarray, cp.ndarray)):
444
487
  if answer[i].dtype != arg.dtype:
445
- raise TypeError("Element " + str(i)
446
- + " of the expected results list is not of the same dtype as the kernel output: "
447
- + str(answer[i].dtype) + " != " + str(arg.dtype) + ".")
488
+ raise TypeError("Element " + str(i) + " of the expected results list is not of the same dtype as the kernel output: " +
489
+ str(answer[i].dtype) + " != " + str(arg.dtype) + ".")
448
490
  if answer[i].size != arg.size:
449
- raise TypeError("Element " + str(i)
450
- + " of the expected results list has a size different from "
451
- + "the kernel argument: "
452
- + str(answer[i].size) + " != " + str(arg.size) + ".")
453
- elif isinstance(answer[i], numpy.number) and isinstance(arg, numpy.number):
491
+ raise TypeError("Element " + str(i) + " of the expected results list has a size different from " + "the kernel argument: " +
492
+ str(answer[i].size) + " != " + str(arg.size) + ".")
493
+ elif isinstance(answer[i], np.number) and isinstance(arg, np.number):
454
494
  if answer[i].dtype != arg.dtype:
455
- raise TypeError("Element " + str(i)
456
- + " of the expected results list is not the same as the kernel output: "
457
- + str(answer[i].dtype) + " != " + str(arg.dtype) + ".")
495
+ raise TypeError("Element " + str(i) + " of the expected results list is not the same as the kernel output: " + str(answer[i].dtype) +
496
+ " != " + str(arg.dtype) + ".")
458
497
  else:
459
498
  #either answer[i] and argument have different types or answer[i] is not a numpy type
460
- if not isinstance(answer[i], numpy.ndarray) or not isinstance(answer[i], numpy.number):
461
- raise TypeError("Element " + str(i)
462
- + " of expected results list is not a numpy array or numpy scalar.")
499
+ if not isinstance(answer[i], np.ndarray) or not isinstance(answer[i], np.number):
500
+ raise TypeError("Element " + str(i) + " of expected results list is not a numpy array or numpy scalar.")
463
501
  else:
464
- raise TypeError("Element " + str(i)
465
- + " of expected results list and kernel arguments have different types.")
502
+ raise TypeError("Element " + str(i) + " of expected results list and kernel arguments have different types.")
466
503
 
467
504
  def _ravel(a):
468
505
  if hasattr(a, 'ravel') and len(a.shape) > 1:
@@ -481,13 +518,16 @@ def _default_verify_function(instance, answer, result_host, atol, verbose):
481
518
 
482
519
  result = _ravel(result_host[i])
483
520
  expected = _flatten(expected)
484
- output_test = numpy.allclose(expected, result, atol=atol)
521
+ if any([isinstance(array, cp.ndarray) for array in [expected, result]]):
522
+ output_test = cp.allclose(expected, result, atol=atol)
523
+ else:
524
+ output_test = np.allclose(expected, result, atol=atol)
485
525
 
486
526
  if not output_test and verbose:
487
527
  print("Error: " + util.get_config_string(instance.params) + " detected during correctness check")
488
- print("this error occured when checking value of the %oth kernel argument" % (i,))
528
+ print("this error occured when checking value of the %oth kernel argument" % (i, ))
489
529
  print("Printing kernel output and expected result, set verbose=False to suppress this debug print")
490
- numpy.set_printoptions(edgeitems=50)
530
+ np.set_printoptions(edgeitems=50)
491
531
  print("Kernel output:")
492
532
  print(result)
493
533
  print("Expected:")
@@ -498,3 +538,85 @@ def _default_verify_function(instance, answer, result_host, atol, verbose):
498
538
  logging.debug('correctness check has found a correctness issue')
499
539
 
500
540
  return correct
541
+
542
+
543
+
544
+ #these functions facilitate compiling templated kernels with PyCuda
545
+ def split_argument_list(argument_list):
546
+ """split all arguments in a list into types and names"""
547
+ regex = r"(.*[\s*]+)(.*)?"
548
+ type_list = []
549
+ name_list = []
550
+ for arg in argument_list:
551
+ match = re.match(regex, arg, re.S)
552
+ if not match:
553
+ raise ValueError("error parsing templated kernel argument list")
554
+ type_list.append(re.sub(r"\s+", " ", match.group(1).strip(), re.S))
555
+ name_list.append(match.group(2).strip())
556
+ return type_list, name_list
557
+
558
+ def apply_template_typenames(type_list, templated_typenames):
559
+ """replace the typename tokens in type_list with their templated typenames"""
560
+ def replace_typename_token(matchobj):
561
+ """function for a whitespace preserving token regex replace"""
562
+ #replace only the match, leaving the whitespace around it as is
563
+ return matchobj.group(1) + templated_typenames[matchobj.group(2)] + matchobj.group(3)
564
+ for i, arg_type in enumerate(type_list):
565
+ for k,v in templated_typenames.items():
566
+ #if the templated typename occurs as a token in the string, meaning that it is enclosed in
567
+ #beginning of string or whitespace, and end of string, whitespace or star
568
+ regex = r"(^|\s+)(" + k + r")($|\s+|\*)"
569
+ sub = re.sub(regex, replace_typename_token, arg_type, re.S)
570
+ type_list[i] = sub
571
+
572
+ def get_templated_typenames(template_parameters, template_arguments):
573
+ """based on the template parameters and arguments, create dict with templated typenames"""
574
+ templated_typenames = {}
575
+ for i, param in enumerate(template_parameters):
576
+ if "typename " in param:
577
+ typename = param[9:]
578
+ templated_typenames[typename] = template_arguments[i]
579
+ return templated_typenames
580
+
581
+ def wrap_templated_kernel(kernel_string, kernel_name):
582
+ """rewrite kernel_string to insert wrapper function for templated kernel"""
583
+ #parse kernel_name to find template_arguments and real kernel name
584
+ name = kernel_name.split("<")[0]
585
+ template_arguments = re.search(r".*?<(.*)>", kernel_name, re.S).group(1).split(',')
586
+
587
+ #parse templated kernel definition
588
+ #relatively strict regex that does not allow nested template parameters like vector<TF>
589
+ #within the template parameter list
590
+ regex = r"template\s*<([^>]*?)>\s*__global__\s+void\s+" + name + r"\s*\((.*?)\)\s*\{"
591
+ match = re.search(regex, kernel_string, re.S)
592
+ if not match:
593
+ raise ValueError("could not find templated kernel definition")
594
+
595
+ template_parameters = match.group(1).split(',')
596
+ argument_list = match.group(2).split(',')
597
+ argument_list = [s.strip() for s in argument_list] #remove extra whitespace around 'type name' strings
598
+
599
+ type_list, name_list = split_argument_list(argument_list)
600
+
601
+ templated_typenames = get_templated_typenames(template_parameters, template_arguments)
602
+ apply_template_typenames(type_list, templated_typenames)
603
+
604
+ #replace __global__ with __device__ in the templated kernel definition
605
+ #could do a more precise replace, but __global__ cannot be used elsewhere in the definition
606
+ definition = match.group(0).replace("__global__", "__device__")
607
+
608
+ #generate code for the compile-time template instantiation
609
+ template_instantiation = f"template __device__ void {kernel_name}(" + ", ".join(type_list) + ");\n"
610
+
611
+ #generate code for the wrapper kernel
612
+ new_arg_list = ", ".join([" ".join((a, b)) for a, b in zip(type_list, name_list)])
613
+ wrapper_function = "\nextern \"C\" __global__ void " + name + "_wrapper(" + new_arg_list + ") {\n " + \
614
+ kernel_name + "(" + ", ".join(name_list) + ");\n}\n"
615
+
616
+ #copy kernel_string, replace definition and append template instantiation and wrapper function
617
+ new_kernel_string = kernel_string[:]
618
+ new_kernel_string = new_kernel_string.replace(match.group(0), definition)
619
+ new_kernel_string += "\n" + template_instantiation
620
+ new_kernel_string += wrapper_function
621
+
622
+ return new_kernel_string, name + "_wrapper"