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.
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/PKG-INFO +4 -3
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/README.rst +3 -2
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/core.py +49 -17
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/cuda.py +70 -27
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/cupy.py +7 -5
- kernel_tuner-0.4.1/kernel_tuner/hyper.py +90 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/integration.py +6 -3
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/interface.py +16 -4
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/opencl.py +3 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/runners/simulation.py +2 -0
- kernel_tuner-0.4.1/kernel_tuner/strategies/bayes_opt.py +837 -0
- 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
- kernel_tuner-0.4.1/kernel_tuner/strategies/bayes_opt_alt_HyperOpt.py +71 -0
- kernel_tuner-0.4.1/kernel_tuner/strategies/bayes_opt_alt_ScikitOpt.py +71 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/genetic_algorithm.py +65 -59
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/minimize.py +8 -6
- kernel_tuner-0.4.1/kernel_tuner/strategies/mls.py +121 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/util.py +97 -108
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner.egg-info/PKG-INFO +4 -3
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner.egg-info/SOURCES.txt +5 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner.egg-info/requires.txt +2 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/setup.py +8 -14
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_cuda_mocked.py +1 -1
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_runners.py +22 -3
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/LICENSE +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/MANIFEST.in +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/__init__.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/c.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/kernelbuilder.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/nvml.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/observers.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/runners/__init__.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/runners/sequential.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/__init__.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/basinhopping.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/brute_force.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/diff_evo.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/firefly_algorithm.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/pso.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/random_sample.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/strategies/simulated_annealing.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner/wrappers.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner.egg-info/dependency_links.txt +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/kernel_tuner.egg-info/top_level.txt +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/setup.cfg +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_c_functions.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_core.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_cuda_functions.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_integration.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_interface.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_kernelbuilder.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_minimize.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_observers.py +0 -0
- {kernel_tuner-0.4.0 → kernel_tuner-0.4.1}/test/test_opencl_functions.py +0 -0
- {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.
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
168
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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))
|
|
65
|
+
self.times.append(self.end.time_since(self.start)) #ms
|
|
38
66
|
|
|
39
67
|
def get_results(self):
|
|
40
|
-
results = {
|
|
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
|
|
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(
|
|
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'):
|
|
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
|
-
|
|
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
|
-
|
|
174
|
-
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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 = [
|
|
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 = {
|
|
384
|
-
|
|
385
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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):
|