kernel-tuner 0.4.0__tar.gz → 0.4.1__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.4.0 → kernel_tuner-0.4.1}/PKG-INFO +4 -3
  2. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/README.rst +3 -2
  3. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/core.py +49 -17
  4. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/cuda.py +70 -27
  5. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/cupy.py +7 -5
  6. kernel_tuner-0.4.1/kernel_tuner/hyper.py +90 -0
  7. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/integration.py +6 -3
  8. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/interface.py +16 -4
  9. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/opencl.py +3 -0
  10. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/runners/simulation.py +2 -0
  11. kernel_tuner-0.4.1/kernel_tuner/strategies/bayes_opt.py +837 -0
  12. kernel_tuner-0.4.0/kernel_tuner/strategies/bayes_opt.py → kernel_tuner-0.4.1/kernel_tuner/strategies/bayes_opt_alt_BayesOpt.py +16 -30
  13. kernel_tuner-0.4.1/kernel_tuner/strategies/bayes_opt_alt_HyperOpt.py +71 -0
  14. kernel_tuner-0.4.1/kernel_tuner/strategies/bayes_opt_alt_ScikitOpt.py +71 -0
  15. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/genetic_algorithm.py +65 -59
  16. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/minimize.py +8 -6
  17. kernel_tuner-0.4.1/kernel_tuner/strategies/mls.py +121 -0
  18. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/util.py +97 -108
  19. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner.egg-info/PKG-INFO +4 -3
  20. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner.egg-info/SOURCES.txt +5 -0
  21. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner.egg-info/requires.txt +2 -0
  22. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/setup.py +8 -14
  23. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_cuda_mocked.py +1 -1
  24. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_runners.py +22 -3
  25. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/LICENSE +0 -0
  26. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/MANIFEST.in +0 -0
  27. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/__init__.py +0 -0
  28. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/c.py +0 -0
  29. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/kernelbuilder.py +0 -0
  30. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/nvml.py +0 -0
  31. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/observers.py +0 -0
  32. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/runners/__init__.py +0 -0
  33. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/runners/sequential.py +0 -0
  34. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/__init__.py +0 -0
  35. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/basinhopping.py +0 -0
  36. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/brute_force.py +0 -0
  37. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/diff_evo.py +0 -0
  38. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/firefly_algorithm.py +0 -0
  39. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/pso.py +0 -0
  40. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/random_sample.py +0 -0
  41. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/simulated_annealing.py +0 -0
  42. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/wrappers.py +0 -0
  43. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner.egg-info/dependency_links.txt +0 -0
  44. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner.egg-info/top_level.txt +0 -0
  45. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/setup.cfg +0 -0
  46. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_c_functions.py +0 -0
  47. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_core.py +0 -0
  48. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_cuda_functions.py +0 -0
  49. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_integration.py +0 -0
  50. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_interface.py +0 -0
  51. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_kernelbuilder.py +0 -0
  52. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_minimize.py +0 -0
  53. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_observers.py +0 -0
  54. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_opencl_functions.py +0 -0
  55. {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_util_functions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kernel_tuner
3
- Version: 0.4.0
3
+ Version: 0.4.1
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
@@ -172,8 +172,9 @@ Description: Kernel Tuner
172
172
  You may also like `CLTune <https://github.com/CNugteren/CLTune>`__ by
173
173
  Cedric Nugteren. CLTune is a C++ library for kernel tuning.
174
174
 
175
- .. |Build Status| image:: https://api.travis-ci.org/benvanwerkhoven/kernel_tuner.svg?branch=master
176
- :target: https://travis-ci.org/benvanwerkhoven/kernel_tuner
175
+
176
+ .. |Build Status| image:: https://github.com/benvanwerkhoven/kernel_tuner/actions/workflows/python-app.yml/badge.svg
177
+ :target: https://github.com/benvanwerkhoven/kernel_tuner/actions/workflows/python-app.yml
177
178
  .. |CodeCov Badge| image:: https://codecov.io/gh/benvanwerkhoven/kernel_tuner/branch/master/graph/badge.svg
178
179
  :target: https://codecov.io/gh/benvanwerkhoven/kernel_tuner
179
180
  .. |PyPi Badge| image:: https://img.shields.io/pypi/v/kernel_tuner.svg?colorB=blue
@@ -164,8 +164,9 @@ Related work
164
164
  You may also like `CLTune <https://github.com/CNugteren/CLTune>`__ by
165
165
  Cedric Nugteren. CLTune is a C++ library for kernel tuning.
166
166
 
167
- .. |Build Status| image:: https://api.travis-ci.org/benvanwerkhoven/kernel_tuner.svg?branch=master
168
- :target: https://travis-ci.org/benvanwerkhoven/kernel_tuner
167
+
168
+ .. |Build Status| image:: https://github.com/benvanwerkhoven/kernel_tuner/actions/workflows/python-app.yml/badge.svg
169
+ :target: https://github.com/benvanwerkhoven/kernel_tuner/actions/workflows/python-app.yml
169
170
  .. |CodeCov Badge| image:: https://codecov.io/gh/benvanwerkhoven/kernel_tuner/branch/master/graph/badge.svg
170
171
  :target: https://codecov.io/gh/benvanwerkhoven/kernel_tuner
171
172
  .. |PyPi Badge| image:: https://img.shields.io/pypi/v/kernel_tuner.svg?colorB=blue
@@ -19,6 +19,14 @@ from kernel_tuner.c import CFunctions
19
19
  from kernel_tuner.nvml import NVMLObserver
20
20
  import kernel_tuner.util as util
21
21
 
22
+
23
+ try:
24
+ import torch
25
+ except ImportError:
26
+ torch = util.TorchPlaceHolder()
27
+
28
+
29
+
22
30
  _KernelInstance = namedtuple("_KernelInstance", ["name", "kernel_source", "kernel_string", "temp_files", "threads", "grid", "params", "arguments"])
23
31
 
24
32
 
@@ -230,7 +238,7 @@ class DeviceInterface(object):
230
238
  elif lang == "C":
231
239
  dev = CFunctions(compiler=compiler, compiler_options=compiler_options, iterations=iterations)
232
240
  else:
233
- raise Exception("Sorry, support for languages other than CUDA, OpenCL, or C is not implemented yet")
241
+ raise ValueError("Sorry, support for languages other than CUDA, OpenCL, or C is not implemented yet")
234
242
 
235
243
  #look for NVMLObserver in observers, if present, enable special tunable parameters through nvml
236
244
  self.use_nvml = False
@@ -244,6 +252,7 @@ class DeviceInterface(object):
244
252
  self.dev = dev
245
253
  self.units = dev.units
246
254
  self.name = dev.name
255
+ self.max_threads = dev.max_threads
247
256
  if not quiet:
248
257
  print("Using: " + self.dev.name)
249
258
 
@@ -282,7 +291,7 @@ class DeviceInterface(object):
282
291
  if any([skip_str in str(e) for skip_str in skippable_exceptions]):
283
292
  logging.debug('benchmark fails due to runtime failure too many resources required')
284
293
  if verbose:
285
- print("skipping config", instance.name, "reason: too many resources requested for launch")
294
+ print(f"skipping config {util.get_instance_string(instance.params)} reason: too many resources requested for launch")
286
295
  else:
287
296
  logging.debug('benchmark encountered runtime failure: ' + str(e))
288
297
  print("Error while benchmarking:", instance.name)
@@ -300,7 +309,7 @@ class DeviceInterface(object):
300
309
  #re-copy original contents of output arguments to GPU memory, to overwrite any changes
301
310
  #by earlier kernel runs
302
311
  for i, arg in enumerate(instance.arguments):
303
- if verify or answer[i] is not None and isinstance(arg, (np.ndarray, cp.ndarray)):
312
+ if (verify or answer[i] is not None) and isinstance(arg, (np.ndarray, cp.ndarray, torch.Tensor)):
304
313
  self.dev.memcpy_htod(gpu_args[i], arg)
305
314
 
306
315
  #run the kernel
@@ -314,6 +323,13 @@ class DeviceInterface(object):
314
323
  if (verify or answer[i] is not None) and isinstance(arg, (np.ndarray, cp.ndarray)):
315
324
  result_host.append(np.zeros_like(arg))
316
325
  self.dev.memcpy_dtoh(result_host[-1], gpu_args[i])
326
+ elif isinstance(arg, torch.Tensor) and isinstance(answer[i], torch.Tensor):
327
+ if not answer[i].is_cuda:
328
+ #if the answer is on the host, copy gpu output to host as well
329
+ result_host.append(torch.zeros_like(answer[i]))
330
+ self.dev.memcpy_dtoh(result_host[-1], gpu_args[i].tensor)
331
+ else:
332
+ result_host.append(gpu_args[i].tensor)
317
333
  else:
318
334
  result_host.append(None)
319
335
 
@@ -324,7 +340,7 @@ class DeviceInterface(object):
324
340
  correct = _default_verify_function(instance, answer, result_host, atol, verbose)
325
341
 
326
342
  if not correct:
327
- raise Exception("Kernel result verification failed for: " + util.get_config_string(instance.params))
343
+ raise RuntimeError("Kernel result verification failed for: " + util.get_config_string(instance.params))
328
344
  return True
329
345
 
330
346
  def compile_and_benchmark(self, kernel_source, gpu_args, params, kernel_options, tuning_options):
@@ -348,9 +364,9 @@ class DeviceInterface(object):
348
364
  if func is None:
349
365
  return None
350
366
 
351
- #add constant memory arguments to compiled module
367
+ #add shared memory arguments to compiled module
352
368
  if kernel_options.smem_args is not None:
353
- self.dev.copy_shared_memory_args(kernel_options.smem_args)
369
+ self.dev.copy_shared_memory_args(util.get_smem_args(kernel_options.smem_args, params))
354
370
  #add constant memory arguments to compiled module
355
371
  if kernel_options.cmem_args is not None:
356
372
  self.dev.copy_constant_memory_args(kernel_options.cmem_args)
@@ -392,30 +408,36 @@ class DeviceInterface(object):
392
408
  if any(msg in str(e) for msg in shared_mem_error_messages):
393
409
  logging.debug('compile_kernel failed due to kernel using too much shared memory')
394
410
  if verbose:
395
- print("skipping config", instance.name, "reason: too much shared memory used")
411
+ print(f"skipping config {util.get_instance_string(instance.params)} reason: too much shared memory used")
396
412
  else:
397
413
  logging.debug('compile_kernel failed due to error: ' + str(e))
398
414
  print("Error while compiling:", instance.name)
399
415
  raise e
400
416
  return func
401
417
 
418
+ def copy_shared_memory_args(self, smem_args):
419
+ """adds shared memory arguments to the most recently compiled module, if using CUDA"""
420
+ if self.lang == "CUDA":
421
+ self.dev.copy_shared_memory_args(smem_args)
422
+ else:
423
+ raise RuntimeError("Error cannot copy shared memory arguments when language is not CUDA")
424
+
402
425
  def copy_constant_memory_args(self, cmem_args):
403
426
  """adds constant memory arguments to the most recently compiled module, if using CUDA"""
404
427
  if self.lang == "CUDA":
405
428
  self.dev.copy_constant_memory_args(cmem_args)
406
429
  else:
407
- raise Exception("Error cannot copy constant memory arguments when language is not CUDA")
430
+ raise RuntimeError("Error cannot copy constant memory arguments when language is not CUDA")
408
431
 
409
432
  def copy_texture_memory_args(self, texmem_args):
410
433
  """adds texture memory arguments to the most recently compiled module, if using CUDA"""
411
434
  if self.lang == "CUDA":
412
435
  self.dev.copy_texture_memory_args(texmem_args)
413
436
  else:
414
- raise Exception("Error cannot copy texture memory arguments when language is not CUDA")
437
+ raise RuntimeError("Error cannot copy texture memory arguments when language is not CUDA")
415
438
 
416
439
  def create_kernel_instance(self, kernel_source, kernel_options, params, verbose):
417
440
  """create kernel instance from kernel source, parameters, problem size, grid divisors, and so on"""
418
- instance_string = util.get_instance_string(params)
419
441
  grid_div = (kernel_options.grid_div_x, kernel_options.grid_div_y, kernel_options.grid_div_z)
420
442
 
421
443
  #insert default block_size_names if needed
@@ -426,7 +448,7 @@ class DeviceInterface(object):
426
448
  threads, grid = util.setup_block_and_grid(kernel_options.problem_size, grid_div, params, kernel_options.block_size_names)
427
449
  if np.prod(threads) > self.dev.max_threads:
428
450
  if verbose:
429
- print("skipping config", instance_string, "reason: too many threads per block")
451
+ print(f"skipping config {util.get_instance_string(params)} reason: too many threads per block")
430
452
  return None
431
453
 
432
454
  #obtain the kernel_string and prepare additional files, if any
@@ -485,21 +507,29 @@ def _default_verify_function(instance, answer, result_host, atol, verbose):
485
507
  if answer[i] is not None: #skip None elements in the answer list
486
508
  if isinstance(answer[i], (np.ndarray, cp.ndarray)) and isinstance(arg, (np.ndarray, cp.ndarray)):
487
509
  if answer[i].dtype != arg.dtype:
488
- raise TypeError("Element " + str(i) + " of the expected results list is not of the same dtype as the kernel output: " +
510
+ raise TypeError(f"Element {i} of the expected results list is not of the same dtype as the kernel output: " +
489
511
  str(answer[i].dtype) + " != " + str(arg.dtype) + ".")
490
512
  if answer[i].size != arg.size:
491
- raise TypeError("Element " + str(i) + " of the expected results list has a size different from " + "the kernel argument: " +
513
+ raise TypeError(f"Element {i} of the expected results list has a size different from " + "the kernel argument: " +
492
514
  str(answer[i].size) + " != " + str(arg.size) + ".")
515
+ elif isinstance(answer[i], torch.Tensor) and isinstance(arg, torch.Tensor):
516
+ if answer[i].dtype != arg.dtype:
517
+ raise TypeError(f"Element {i} of the expected results list is not of the same dtype as the kernel output: " +
518
+ str(answer[i].dtype) + " != " + str(arg.dtype) + ".")
519
+ if answer[i].size() != arg.size():
520
+ raise TypeError(f"Element {i} of the expected results list has a size different from " + "the kernel argument: " +
521
+ str(answer[i].size) + " != " + str(arg.size) + ".")
522
+
493
523
  elif isinstance(answer[i], np.number) and isinstance(arg, np.number):
494
524
  if answer[i].dtype != 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) +
525
+ raise TypeError(f"Element {i} of the expected results list is not the same as the kernel output: " + str(answer[i].dtype) +
496
526
  " != " + str(arg.dtype) + ".")
497
527
  else:
498
528
  #either answer[i] and argument have different types or answer[i] is not a numpy type
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.")
529
+ if not isinstance(answer[i], (np.ndarray, cp.ndarray, torch.Tensor)) or not isinstance(answer[i], np.number):
530
+ raise TypeError(f"Element {i} of expected results list is not a numpy/cupy ndarray, torch Tensor or numpy scalar.")
501
531
  else:
502
- raise TypeError("Element " + str(i) + " of expected results list and kernel arguments have different types.")
532
+ raise TypeError(f"Element {i} of expected results list and kernel arguments have different types.")
503
533
 
504
534
  def _ravel(a):
505
535
  if hasattr(a, 'ravel') and len(a.shape) > 1:
@@ -520,6 +550,8 @@ def _default_verify_function(instance, answer, result_host, atol, verbose):
520
550
  expected = _flatten(expected)
521
551
  if any([isinstance(array, cp.ndarray) for array in [expected, result]]):
522
552
  output_test = cp.allclose(expected, result, atol=atol)
553
+ elif isinstance(expected, torch.Tensor) and isinstance(result, torch.Tensor):
554
+ output_test = torch.allclose(expected, result, atol=atol)
523
555
  else:
524
556
  output_test = np.allclose(expected, result, atol=atol)
525
557
 
@@ -7,13 +7,23 @@ import numpy as np
7
7
 
8
8
  from kernel_tuner.observers import BenchmarkObserver
9
9
  from kernel_tuner.nvml import nvml
10
+ from kernel_tuner.util import TorchPlaceHolder
10
11
 
11
12
  #embedded in try block to be able to generate documentation
12
13
  #and run tests without pycuda installed
13
14
  try:
14
15
  import pycuda.driver as drv
16
+ pycuda_available = True
15
17
  except ImportError:
16
- drv = None
18
+
19
+ class PyCudaPlaceHolder():
20
+
21
+ def __init__(self):
22
+ self.PointerHolderBase = object
23
+
24
+ drv = PyCudaPlaceHolder()
25
+ pycuda_available = False
26
+
17
27
  try:
18
28
  from pycuda.compiler import SourceModule
19
29
  except ImportError:
@@ -23,9 +33,27 @@ try:
23
33
  except ImportError:
24
34
  DynamicSourceModule = None
25
35
 
36
+ try:
37
+ import torch
38
+ except ImportError:
39
+ torch = TorchPlaceHolder()
40
+
41
+
42
+ class Holder(drv.PointerHolderBase):
43
+ """ class to interoperate torch device memory allocations with PyCUDA """
44
+
45
+ def __init__(self, tensor):
46
+ super(Holder, self).__init__()
47
+ self.tensor = tensor
48
+ self.gpudata = tensor.data_ptr()
49
+
50
+ def get_pointer(self):
51
+ return self.t.data_ptr()
52
+
26
53
 
27
54
  class CudaRuntimeObserver(BenchmarkObserver):
28
55
  """ Observer that measures time using CUDA events during benchmarking """
56
+
29
57
  def __init__(self, dev):
30
58
  self.dev = dev
31
59
  self.stream = dev.stream
@@ -34,10 +62,13 @@ class CudaRuntimeObserver(BenchmarkObserver):
34
62
  self.times = []
35
63
 
36
64
  def after_finish(self):
37
- self.times.append(self.end.time_since(self.start)) #ms
65
+ self.times.append(self.end.time_since(self.start)) #ms
38
66
 
39
67
  def get_results(self):
40
- results = {"time": np.average(self.times), "times": self.times.copy()}
68
+ results = {
69
+ "time": np.average(self.times),
70
+ "times": self.times.copy()
71
+ }
41
72
  self.times = []
42
73
  return results
43
74
 
@@ -61,19 +92,20 @@ class CudaFunctions(object):
61
92
  """
62
93
  self.allocations = []
63
94
  self.texrefs = []
64
- if not drv:
95
+ if not pycuda_available and isinstance(drv, PyCudaPlaceHolder): #and part to allow mocking
65
96
  raise ImportError("Error: pycuda not installed, please install e.g. using 'pip install pycuda'.")
66
97
 
67
98
  drv.init()
68
99
  self.context = drv.Device(device).make_context()
69
100
 
70
101
  #inspect device properties
71
- devprops = {str(k): v for (k, v) in self.context.get_device().get_attributes().items()}
102
+ devprops = {str(k): v
103
+ for (k, v) in self.context.get_device().get_attributes().items()}
72
104
  self.max_threads = devprops['MAX_THREADS_PER_BLOCK']
73
105
  cc = str(devprops.get('COMPUTE_CAPABILITY_MAJOR', '0')) + str(devprops.get('COMPUTE_CAPABILITY_MINOR', '0'))
74
106
  if cc == "00":
75
107
  cc = self.context.get_device().compute_capability()
76
- self.cc = str(cc[0])+str(cc[1])
108
+ self.cc = str(cc[0]) + str(cc[1])
77
109
  self.iterations = iterations
78
110
  self.current_module = None
79
111
  self.func = None
@@ -85,7 +117,8 @@ class CudaFunctions(object):
85
117
  else:
86
118
  self.source_mod = SourceModule
87
119
  if not self.source_mod:
88
- raise ImportError("Error: pycuda not correctly installed, please ensure pycuda is installed on the same CUDA installation as you're using right now")
120
+ raise ImportError(
121
+ "Error: pycuda not correctly installed, please ensure pycuda is installed on the same CUDA installation as you're using right now")
89
122
 
90
123
  #create a stream and events
91
124
  self.stream = drv.Stream()
@@ -117,7 +150,7 @@ class CudaFunctions(object):
117
150
 
118
151
  def __exit__(self, *exc):
119
152
  for gpu_mem in self.allocations:
120
- if hasattr(gpu_mem, 'free'): #if needed for when using mocks during testing
153
+ if hasattr(gpu_mem, 'free'): #if needed for when using mocks during testing
121
154
  gpu_mem.free()
122
155
  if hasattr(self, 'context'):
123
156
  self.context.pop()
@@ -141,11 +174,15 @@ class CudaFunctions(object):
141
174
  self.allocations.append(alloc)
142
175
  gpu_args.append(alloc)
143
176
  drv.memcpy_htod(gpu_args[-1], arg)
144
- else: # if not an array, just pass argument along
177
+ elif isinstance(arg, torch.Tensor):
178
+ if arg.is_cuda:
179
+ gpu_args.append(Holder(arg))
180
+ else:
181
+ gpu_args.append(Holder(arg.cuda()))
182
+ else: # if not an array, just pass argument along
145
183
  gpu_args.append(arg)
146
184
  return gpu_args
147
185
 
148
-
149
186
  def compile(self, kernel_instance):
150
187
  """call the CUDA compiler to compile the kernel, return the device function
151
188
 
@@ -170,9 +207,8 @@ class CudaFunctions(object):
170
207
  compiler_options += self.compiler_options
171
208
 
172
209
  self.current_module = self.source_mod(kernel_string, options=compiler_options + ["-e", kernel_name],
173
- arch=('compute_' + self.cc) if self.cc != "00" else None,
174
- code=('sm_' + self.cc) if self.cc != "00" else None,
175
- cache_dir=False, no_extern_c=no_extern_c)
210
+ arch=('compute_' + self.cc) if self.cc != "00" else None, code=('sm_' + self.cc) if self.cc != "00" else None,
211
+ cache_dir=False, no_extern_c=no_extern_c)
176
212
 
177
213
  self.func = self.current_module.get_function(kernel_name)
178
214
  return self.func
@@ -182,7 +218,6 @@ class CudaFunctions(object):
182
218
  else:
183
219
  raise e
184
220
 
185
-
186
221
  def benchmark(self, func, gpu_args, threads, grid):
187
222
  """runs the kernel and measures time repeatedly, returns average time
188
223
 
@@ -215,9 +250,10 @@ class CudaFunctions(object):
215
250
  result = dict()
216
251
  self.context.synchronize()
217
252
  for _ in range(self.iterations):
218
- self.start.record(stream=self.stream)
219
253
  for obs in self.observers:
220
254
  obs.before_start()
255
+ self.context.synchronize()
256
+ self.start.record(stream=self.stream)
221
257
  self.run_kernel(func, gpu_args, threads, grid, stream=self.stream)
222
258
  self.end.record(stream=self.stream)
223
259
  for obs in self.observers:
@@ -225,6 +261,7 @@ class CudaFunctions(object):
225
261
  while not self.end.query():
226
262
  for obs in self.observers:
227
263
  obs.during()
264
+ time.sleep(1e-6) #one microsecond
228
265
  self.end.synchronize()
229
266
  for obs in self.observers:
230
267
  obs.after_finish()
@@ -267,13 +304,16 @@ class CudaFunctions(object):
267
304
  :type texmem_args: dict
268
305
  """
269
306
 
270
-
271
- filter_mode_map = { 'point': drv.filter_mode.POINT,
272
- 'linear': drv.filter_mode.LINEAR }
273
- address_mode_map = { 'border': drv.address_mode.BORDER,
274
- 'clamp': drv.address_mode.CLAMP,
275
- 'mirror': drv.address_mode.MIRROR,
276
- 'wrap': drv.address_mode.WRAP }
307
+ filter_mode_map = {
308
+ 'point': drv.filter_mode.POINT,
309
+ 'linear': drv.filter_mode.LINEAR
310
+ }
311
+ address_mode_map = {
312
+ 'border': drv.address_mode.BORDER,
313
+ 'clamp': drv.address_mode.CLAMP,
314
+ 'mirror': drv.address_mode.MIRROR,
315
+ 'wrap': drv.address_mode.WRAP
316
+ }
277
317
 
278
318
  logging.debug('copy_texture_memory_args called')
279
319
  logging.debug('current module: ' + str(self.current_module))
@@ -299,7 +339,7 @@ class CudaFunctions(object):
299
339
  # address_mode is set per axis
300
340
  amode = v['address_mode']
301
341
  if not isinstance(amode, list):
302
- amode = [ amode ] * data.ndim
342
+ amode = [amode] * data.ndim
303
343
  for i, m in enumerate(amode):
304
344
  try:
305
345
  if m is not None:
@@ -351,7 +391,6 @@ class CudaFunctions(object):
351
391
  """
352
392
  drv.memset_d8(allocation, value, size)
353
393
 
354
-
355
394
  def memcpy_dtoh(self, dest, src):
356
395
  """perform a device to host memory copy
357
396
 
@@ -363,6 +402,8 @@ class CudaFunctions(object):
363
402
  """
364
403
  if isinstance(src, drv.DeviceAllocation):
365
404
  drv.memcpy_dtoh(dest, src)
405
+ elif isinstance(src, torch.Tensor):
406
+ dest[:] = src
366
407
  else:
367
408
  dest = src
368
409
 
@@ -380,6 +421,8 @@ class CudaFunctions(object):
380
421
  else:
381
422
  dest = src
382
423
 
383
- units = {'time': 'ms',
384
- 'power': 's,mW',
385
- 'energy': 'J'}
424
+ units = {
425
+ 'time': 'ms',
426
+ 'power': 's,mW',
427
+ 'energy': 'J'
428
+ }
@@ -1,14 +1,12 @@
1
1
  """This module contains all Cupy specific kernel_tuner functions"""
2
2
  from __future__ import print_function
3
3
 
4
- import re
5
4
 
6
5
  import logging
7
6
  import time
8
7
  import numpy as np
9
8
 
10
9
  from kernel_tuner.observers import BenchmarkObserver
11
- from kernel_tuner.nvml import nvml
12
10
 
13
11
  #embedded in try block to be able to generate documentation
14
12
  #and run tests without cupy installed
@@ -36,7 +34,7 @@ class CupyRuntimeObserver(BenchmarkObserver):
36
34
  return results
37
35
 
38
36
 
39
- class CupyFunctions(object):
37
+ class CupyFunctions:
40
38
  """Class that groups the Cupy functions on maintains state about the device"""
41
39
 
42
40
  def __init__(self, device=0, iterations=7, compiler_options=None, observers=None):
@@ -56,7 +54,8 @@ class CupyFunctions(object):
56
54
  self.allocations = []
57
55
  self.texrefs = []
58
56
  if not cp:
59
- raise ImportError("Error: cupy not installed, please install e.g. using 'pip install cupy-cuda111', please check https://github.com/cupy/cupy.")
57
+ raise ImportError("Error: cupy not installed, please install e.g. " +
58
+ "using 'pip install cupy-cuda111', please check https://github.com/cupy/cupy.")
60
59
 
61
60
  #select device
62
61
  self.dev = dev = cp.cuda.Device(device).__enter__()
@@ -68,6 +67,7 @@ class CupyFunctions(object):
68
67
 
69
68
  self.iterations = iterations
70
69
  self.current_module = None
70
+ self.func = None
71
71
  self.compiler_options = compiler_options or []
72
72
 
73
73
  #create a stream and events
@@ -189,9 +189,10 @@ class CupyFunctions(object):
189
189
  result = dict()
190
190
  self.dev.synchronize()
191
191
  for _ in range(self.iterations):
192
- self.start.record(stream=self.stream)
193
192
  for obs in self.observers:
194
193
  obs.before_start()
194
+ self.dev.synchronize()
195
+ self.start.record(stream=self.stream)
195
196
  self.run_kernel(func, gpu_args, threads, grid, stream=self.stream)
196
197
  self.end.record(stream=self.stream)
197
198
  for obs in self.observers:
@@ -199,6 +200,7 @@ class CupyFunctions(object):
199
200
  while not self.end.done:
200
201
  for obs in self.observers:
201
202
  obs.during()
203
+ time.sleep(1e-6)
202
204
  for obs in self.observers:
203
205
  obs.after_finish()
204
206
 
@@ -0,0 +1,90 @@
1
+ """ Module for functions related to hyperparameter optimization """
2
+
3
+ import itertools
4
+ import numpy as np
5
+ import kernel_tuner
6
+ import warnings
7
+
8
+ from kernel_tuner.util import get_config_string
9
+
10
+
11
+ def tune_hyper_params(target_strategy, hyper_params, *args, **kwargs):
12
+ """ Tune hyperparameters for a given strategy and kernel
13
+
14
+ This function is to be called just like tune_kernel, except that you specify a strategy
15
+ and a dictionary with hyperparameters in front of the arguments you pass to tune_kernel.
16
+
17
+ The arguments to tune_kernel should contain a cachefile. To compute the optimum the hyperparameter
18
+ tuner first tunes the kernel with a brute force search. If your cachefile is not yet complete
19
+ this may take very long.
20
+
21
+ :param target_strategy: Specify the strategy for which to tune hyperparameters
22
+ :type target_strategy: string
23
+
24
+ :param hyper_params: A dictionary containing the hyperparameters as keys and
25
+ lists the possible values per key
26
+ :type hyper_params: dict(string: list)
27
+
28
+ :param args: all positional arguments used to call tune_kernel
29
+ :type args: various
30
+
31
+ :param kwargs: other keyword arguments to pass to tune_kernel
32
+ :type kwargs: dict
33
+
34
+ """
35
+ if not "cache" in kwargs:
36
+ raise ValueError("Please specify a cachefile to store benchmarking data when tuning hyperparameters")
37
+
38
+ def put_if_not_present(d, key, value):
39
+ d[key] = value if not key in d else d[key]
40
+
41
+ put_if_not_present(kwargs, "verbose", False)
42
+ put_if_not_present(kwargs, "quiet", True)
43
+ put_if_not_present(kwargs, "simulation_mode", True)
44
+ kwargs['strategy'] = 'brute_force'
45
+
46
+ #last position argument is tune_params
47
+ tune_params = args[-1]
48
+
49
+ #find optimum
50
+ kwargs["strategy"] = "brute_force"
51
+ results, env = kernel_tuner.tune_kernel(*args, **kwargs)
52
+ optimum = min(results, key=lambda p: p["time"])["time"]
53
+
54
+ #could throw a warning for the kwargs that will be overwritten, strategy(_options)
55
+ kwargs["strategy"] = target_strategy
56
+
57
+ parameter_space = itertools.product(*hyper_params.values())
58
+ all_results = []
59
+
60
+ for params in parameter_space:
61
+ strategy_options = dict(zip(hyper_params.keys(), params))
62
+
63
+ kwargs["strategy_options"] = strategy_options
64
+
65
+ fevals = []
66
+ p_of_opt = []
67
+ for _ in range(100):
68
+ #measure
69
+ with warnings.catch_warnings():
70
+ warnings.simplefilter("ignore")
71
+ results, env = kernel_tuner.tune_kernel(*args, **kwargs)
72
+
73
+ #get unique function evaluations
74
+ unique_fevals = {",".join([str(v) for k, v in record.items() if k in tune_params])
75
+ for record in results}
76
+
77
+ fevals.append(len(unique_fevals))
78
+ # p_of_opt.append(optimum / min(results, key=lambda p: p["time"])["time"] * 100)
79
+ p_of_opt.append(min(results, key=lambda p: p["time"])["time"] / optimum * 100)
80
+
81
+ strategy_options["fevals"] = np.average(fevals)
82
+ strategy_options["fevals_std"] = np.std(fevals)
83
+
84
+ strategy_options["p_of_opt"] = np.average(p_of_opt)
85
+ strategy_options["p_of_opt_std"] = np.std(p_of_opt)
86
+
87
+ print(get_config_string(strategy_options))
88
+ all_results.append(strategy_options)
89
+
90
+ return all_results
@@ -197,8 +197,11 @@ def store_results(results_filename, kernel_name, kernel_string, tune_params, pro
197
197
  meta = {}
198
198
  meta["version_number"] = "1.0"
199
199
  meta["kernel_name"] = kernel_name
200
- if kernel_string:
201
- meta["kernel_string"] = kernel_string
200
+ if kernel_string and not callable(kernel_string) and not isinstance(kernel_string, list):
201
+ if util.looks_like_a_filename(kernel_string):
202
+ meta["kernel_string"] = util.read_file(kernel_string)
203
+ else:
204
+ meta["kernel_string"] = kernel_string
202
205
  meta["objective"] = objective
203
206
  meta["objective_higher_is_better"] = objective_higher_is_better
204
207
  meta["tunable_parameters"] = list(tune_params.keys())
@@ -229,7 +232,7 @@ def store_results(results_filename, kernel_name, kernel_string, tune_params, pro
229
232
  #write output file
230
233
  meta["data"] = data
231
234
  with open(results_filename, 'w') as fh:
232
- fh.write(json.dumps(meta))
235
+ fh.write(json.dumps(meta, indent=""))
233
236
 
234
237
 
235
238
  def create_device_targets(header_filename, results_filename, objective=None, objective_higher_is_better=None):