kernel-tuner 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/PKG-INFO +11 -11
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/README.rst +7 -7
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/c.py +25 -15
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/core.py +8 -4
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/cuda.py +21 -4
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/interface.py +19 -11
- kernel_tuner-0.3.2/kernel_tuner/kernelbuilder.py +98 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/opencl.py +3 -3
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/runners/sequential.py +18 -5
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/bayes_opt.py +12 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/minimize.py +14 -5
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/util.py +98 -14
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/wrappers.py +1 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/PKG-INFO +11 -11
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/SOURCES.txt +1 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/setup.py +1 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_c_functions.py +36 -3
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_core.py +1 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_cuda_functions.py +7 -21
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_cuda_mocked.py +6 -4
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_interface.py +10 -10
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_minimize.py +1 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_opencl_functions.py +6 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_util_functions.py +40 -3
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/LICENSE +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/MANIFEST.in +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/__init__.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/nvml.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/runners/__init__.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/__init__.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/basinhopping.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/brute_force.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/diff_evo.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/firefly_algorithm.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/genetic_algorithm.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/pso.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/random_sample.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner/strategies/simulated_annealing.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/dependency_links.txt +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/requires.txt +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/kernel_tuner.egg-info/top_level.txt +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/setup.cfg +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.2}/test/test_runners.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kernel_tuner
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: An easy to use CUDA/OpenCL kernel tuner in Python
|
|
5
5
|
Home-page: http://benvanwerkhoven.github.io/kernel_tuner/
|
|
6
6
|
Author: Ben van Werkhoven
|
|
@@ -9,7 +9,7 @@ License: Apache 2.0
|
|
|
9
9
|
Description: Kernel Tuner
|
|
10
10
|
============
|
|
11
11
|
|
|
12
|
-
|Build Status| |CodeCov Badge| |PyPi Badge|
|
|
12
|
+
|Build Status| |CodeCov Badge| |PyPi Badge| |Zenodo Badge| |SonarCloud Badge| |FairSoftware Badge|
|
|
13
13
|
|
|
14
14
|
Kernel Tuner simplifies the software development of optimized and auto-tuned GPU programs, by enabling Python-based unit testing of GPU code and making it easy to develop scripts for auto-tuning GPU kernels. This also means no extensive changes and no new dependencies are required in the kernel code. The kernels can still be compiled and used as normal from any host programming language.
|
|
15
15
|
|
|
@@ -178,12 +178,12 @@ Description: Kernel Tuner
|
|
|
178
178
|
:target: https://codecov.io/gh/benvanwerkhoven/kernel_tuner
|
|
179
179
|
.. |PyPi Badge| image:: https://img.shields.io/pypi/v/kernel_tuner.svg?colorB=blue
|
|
180
180
|
:target: https://pypi.python.org/pypi/kernel_tuner/
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
.. |Zenodo Badge| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.1220113.svg
|
|
182
|
+
:target: https://doi.org/10.5281/zenodo.1220113
|
|
183
|
+
.. |SonarCloud Badge| image:: https://sonarcloud.io/api/project_badges/measure?project=benvanwerkhoven_kernel_tuner&metric=alert_status
|
|
184
|
+
:target: https://sonarcloud.io/dashboard?id=benvanwerkhoven_kernel_tuner
|
|
185
|
+
.. |FairSoftware Badge| image:: https://img.shields.io/badge/fair--software.eu-%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F-green
|
|
186
|
+
:target: https://fair-software.eu
|
|
187
187
|
|
|
188
188
|
Keywords: auto-tuning gpu computing pycuda cuda pyopencl opencl
|
|
189
189
|
Platform: UNKNOWN
|
|
@@ -201,9 +201,9 @@ Classifier: Topic :: Scientific/Engineering
|
|
|
201
201
|
Classifier: Topic :: Software Development
|
|
202
202
|
Classifier: Topic :: System :: Distributed Computing
|
|
203
203
|
Classifier: Development Status :: 4 - Beta
|
|
204
|
-
Provides-Extra: dev
|
|
205
204
|
Provides-Extra: doc
|
|
206
|
-
Provides-Extra: cuda_opencl
|
|
207
|
-
Provides-Extra: tutorial
|
|
208
205
|
Provides-Extra: cuda
|
|
209
206
|
Provides-Extra: opencl
|
|
207
|
+
Provides-Extra: cuda_opencl
|
|
208
|
+
Provides-Extra: tutorial
|
|
209
|
+
Provides-Extra: dev
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Kernel Tuner
|
|
2
2
|
============
|
|
3
3
|
|
|
4
|
-
|Build Status| |CodeCov Badge| |PyPi Badge|
|
|
4
|
+
|Build Status| |CodeCov Badge| |PyPi Badge| |Zenodo Badge| |SonarCloud Badge| |FairSoftware Badge|
|
|
5
5
|
|
|
6
6
|
Kernel Tuner simplifies the software development of optimized and auto-tuned GPU programs, by enabling Python-based unit testing of GPU code and making it easy to develop scripts for auto-tuning GPU kernels. This also means no extensive changes and no new dependencies are required in the kernel code. The kernels can still be compiled and used as normal from any host programming language.
|
|
7
7
|
|
|
@@ -170,9 +170,9 @@ Cedric Nugteren. CLTune is a C++ library for kernel tuning.
|
|
|
170
170
|
:target: https://codecov.io/gh/benvanwerkhoven/kernel_tuner
|
|
171
171
|
.. |PyPi Badge| image:: https://img.shields.io/pypi/v/kernel_tuner.svg?colorB=blue
|
|
172
172
|
:target: https://pypi.python.org/pypi/kernel_tuner/
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
173
|
+
.. |Zenodo Badge| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.1220113.svg
|
|
174
|
+
:target: https://doi.org/10.5281/zenodo.1220113
|
|
175
|
+
.. |SonarCloud Badge| image:: https://sonarcloud.io/api/project_badges/measure?project=benvanwerkhoven_kernel_tuner&metric=alert_status
|
|
176
|
+
:target: https://sonarcloud.io/dashboard?id=benvanwerkhoven_kernel_tuner
|
|
177
|
+
.. |FairSoftware Badge| image:: https://img.shields.io/badge/fair--software.eu-%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F-green
|
|
178
|
+
:target: https://fair-software.eu
|
|
@@ -90,22 +90,24 @@ class CFunctions(object):
|
|
|
90
90
|
if not isinstance(arg, (numpy.ndarray, numpy.number)):
|
|
91
91
|
raise TypeError("Argument is not numpy ndarray or numpy scalar %s" % type(arg))
|
|
92
92
|
dtype_str = str(arg.dtype)
|
|
93
|
-
data = arg.copy()
|
|
93
|
+
#data = arg.copy()
|
|
94
94
|
if isinstance(arg, numpy.ndarray):
|
|
95
95
|
if dtype_str in dtype_map.keys():
|
|
96
96
|
# In numpy <= 1.15, ndarray.ctypes.data_as does not itself keep a reference
|
|
97
97
|
# to its underlying array, so we need to store a reference to arg.copy()
|
|
98
98
|
# in the Argument object manually to avoid it being deleted.
|
|
99
99
|
# (This changed in numpy > 1.15.)
|
|
100
|
-
data_ctypes = data.ctypes.data_as(C.POINTER(dtype_map[dtype_str]))
|
|
100
|
+
#data_ctypes = data.ctypes.data_as(C.POINTER(dtype_map[dtype_str]))
|
|
101
|
+
data_ctypes = arg.ctypes.data_as(C.POINTER(dtype_map[dtype_str]))
|
|
101
102
|
else:
|
|
102
103
|
raise TypeError("unknown dtype for ndarray")
|
|
103
104
|
elif isinstance(arg, numpy.generic):
|
|
104
105
|
data_ctypes = dtype_map[dtype_str](arg)
|
|
105
|
-
ctype_args[i] = Argument(numpy=data, ctypes=data_ctypes)
|
|
106
|
+
#ctype_args[i] = Argument(numpy=data, ctypes=data_ctypes)
|
|
107
|
+
ctype_args[i] = Argument(numpy=arg, ctypes=data_ctypes)
|
|
106
108
|
return ctype_args
|
|
107
109
|
|
|
108
|
-
def compile(self,
|
|
110
|
+
def compile(self, kernel_instance):
|
|
109
111
|
"""call the C compiler to compile the kernel, return the function
|
|
110
112
|
|
|
111
113
|
:param kernel_name: The name of the kernel to be compiled, used to lookup the
|
|
@@ -118,7 +120,9 @@ class CFunctions(object):
|
|
|
118
120
|
:returns: An ctypes function that can be called directly.
|
|
119
121
|
:rtype: ctypes._FuncPtr
|
|
120
122
|
"""
|
|
121
|
-
logging.debug('compiling ' +
|
|
123
|
+
logging.debug('compiling ' + kernel_instance.name)
|
|
124
|
+
|
|
125
|
+
kernel_string = kernel_instance.kernel_string
|
|
122
126
|
|
|
123
127
|
if self.lib != None:
|
|
124
128
|
self.cleanup_lib()
|
|
@@ -134,25 +138,31 @@ class CFunctions(object):
|
|
|
134
138
|
else:
|
|
135
139
|
compiler_options.append("-fopenmp")
|
|
136
140
|
|
|
137
|
-
#
|
|
141
|
+
#if filename is known, use that one
|
|
142
|
+
suffix = kernel_instance.kernel_source.get_user_suffix()
|
|
138
143
|
|
|
139
|
-
#
|
|
140
|
-
|
|
144
|
+
#if code contains device code, suffix .cu is required
|
|
145
|
+
device_code_signals = ["__global", "__syncthreads()", "threadIdx"]
|
|
146
|
+
if any([snippet in kernel_string for snippet in device_code_signals]):
|
|
147
|
+
suffix = ".cu"
|
|
141
148
|
|
|
142
149
|
#detect whether to use nvcc as default instead of g++, may overrule an explicitly passed g++
|
|
143
|
-
if ("#include <cuda" in kernel_string) or ("cudaMemcpy" in kernel_string):
|
|
150
|
+
if (suffix == ".cu") or ("#include <cuda" in kernel_string) or ("cudaMemcpy" in kernel_string):
|
|
144
151
|
if self.compiler == "g++" and self.nvcc_available:
|
|
145
152
|
self.compiler = "nvcc"
|
|
146
153
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
suffix = ".
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
if suffix is None:
|
|
155
|
+
#select right suffix based on compiler
|
|
156
|
+
suffix = ".cc"
|
|
157
|
+
|
|
158
|
+
if self.compiler in ["gfortran", "pgfortran", "ftn", "ifort"]:
|
|
159
|
+
suffix = ".F90"
|
|
152
160
|
|
|
153
161
|
if self.compiler == "nvcc":
|
|
154
162
|
compiler_options = ["-Xcompiler=" + c for c in compiler_options]
|
|
155
163
|
|
|
164
|
+
#this basically checks if we aren't compiling Fortran
|
|
165
|
+
#at the moment any C, C++, or CUDA code is assumed to use extern "C" linkage
|
|
156
166
|
if ".c" in suffix:
|
|
157
167
|
if not "extern \"C\"" in kernel_string:
|
|
158
168
|
kernel_string = "extern \"C\" {\n" + kernel_string + "\n}"
|
|
@@ -194,7 +204,7 @@ class CFunctions(object):
|
|
|
194
204
|
|
|
195
205
|
|
|
196
206
|
self.lib = numpy.ctypeslib.load_library(filename, '.')
|
|
197
|
-
func = getattr(self.lib,
|
|
207
|
+
func = getattr(self.lib, kernel_instance.name)
|
|
198
208
|
func.restype = C.c_float
|
|
199
209
|
|
|
200
210
|
finally:
|
|
@@ -115,7 +115,7 @@ class KernelSource(object):
|
|
|
115
115
|
|
|
116
116
|
ks = self.get_kernel_string(i, params)
|
|
117
117
|
# add preprocessor statements
|
|
118
|
-
n, ks = util.prepare_kernel_string(kernel_name, ks, params, grid, threads, block_size_names)
|
|
118
|
+
n, ks = util.prepare_kernel_string(kernel_name, ks, params, grid, threads, block_size_names, self.lang)
|
|
119
119
|
|
|
120
120
|
if i == 0:
|
|
121
121
|
# primary kernel source
|
|
@@ -175,7 +175,7 @@ class KernelSource(object):
|
|
|
175
175
|
class DeviceInterface(object):
|
|
176
176
|
"""Class that offers a High-Level Device Interface to the rest of the Kernel Tuner"""
|
|
177
177
|
|
|
178
|
-
def __init__(self, kernel_source, device=0, platform=0,
|
|
178
|
+
def __init__(self, kernel_source, device=0, platform=0, quiet=False, compiler=None, compiler_options=None, iterations=7):
|
|
179
179
|
""" Instantiate the DeviceInterface, based on language in kernel source
|
|
180
180
|
|
|
181
181
|
:param kernel_source The kernel sources
|
|
@@ -310,6 +310,9 @@ class DeviceInterface(object):
|
|
|
310
310
|
return None
|
|
311
311
|
|
|
312
312
|
#add constant memory arguments to compiled module
|
|
313
|
+
if kernel_options.smem_args is not None:
|
|
314
|
+
self.dev.copy_shared_memory_args(kernel_options.smem_args)
|
|
315
|
+
#add constant memory arguments to compiled module
|
|
313
316
|
if kernel_options.cmem_args is not None:
|
|
314
317
|
self.dev.copy_constant_memory_args(kernel_options.cmem_args)
|
|
315
318
|
#add texture memory arguments to compiled module
|
|
@@ -341,12 +344,13 @@ class DeviceInterface(object):
|
|
|
341
344
|
#compile kernel_string into device func
|
|
342
345
|
func = None
|
|
343
346
|
try:
|
|
344
|
-
func = self.dev.compile(instance
|
|
347
|
+
func = self.dev.compile(instance)
|
|
345
348
|
except Exception as e:
|
|
346
349
|
#compiles may fail because certain kernel configurations use too
|
|
347
350
|
#much shared memory for example, the desired behavior is to simply
|
|
348
351
|
#skip over this configuration and try the next one
|
|
349
|
-
|
|
352
|
+
shared_mem_error_messages = ["uses too much shared data", "local memory limit exceeded"]
|
|
353
|
+
if any(msg in str(e) for msg in shared_mem_error_messages):
|
|
350
354
|
logging.debug('compile_kernel failed due to kernel using too much shared memory')
|
|
351
355
|
if verbose:
|
|
352
356
|
print("skipping config", instance.name, "reason: too much shared memory used")
|
|
@@ -47,7 +47,13 @@ class CudaFunctions(object):
|
|
|
47
47
|
|
|
48
48
|
drv.init()
|
|
49
49
|
self.context = drv.Device(device).make_context()
|
|
50
|
-
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
self.nvml = nvml(device)
|
|
53
|
+
self.nvml.pwr_usage()
|
|
54
|
+
self.use_nvml = True
|
|
55
|
+
except:
|
|
56
|
+
self.use_nvml = False
|
|
51
57
|
|
|
52
58
|
#inspect device properties
|
|
53
59
|
devprops = {str(k): v for (k, v) in self.context.get_device().get_attributes().items()}
|
|
@@ -71,6 +77,9 @@ class CudaFunctions(object):
|
|
|
71
77
|
#create a stream
|
|
72
78
|
self.stream = drv.Stream()
|
|
73
79
|
|
|
80
|
+
#default dynamically allocated shared memory size, can be overwritten using smem_args
|
|
81
|
+
self.smem_size = 0
|
|
82
|
+
|
|
74
83
|
#collect environment information
|
|
75
84
|
env = dict()
|
|
76
85
|
env["device_name"] = self.context.get_device().name()
|
|
@@ -113,7 +122,7 @@ class CudaFunctions(object):
|
|
|
113
122
|
return gpu_args
|
|
114
123
|
|
|
115
124
|
|
|
116
|
-
def compile(self,
|
|
125
|
+
def compile(self, kernel_instance):
|
|
117
126
|
"""call the CUDA compiler to compile the kernel, return the device function
|
|
118
127
|
|
|
119
128
|
:param kernel_name: The name of the kernel to be compiled, used to lookup the
|
|
@@ -126,6 +135,9 @@ class CudaFunctions(object):
|
|
|
126
135
|
:returns: An CUDA kernel that can be called directly.
|
|
127
136
|
:rtype: pycuda.driver.Function
|
|
128
137
|
"""
|
|
138
|
+
kernel_string = kernel_instance.kernel_string
|
|
139
|
+
kernel_name = kernel_instance.name
|
|
140
|
+
|
|
129
141
|
try:
|
|
130
142
|
no_extern_c = 'extern "C"' in kernel_string
|
|
131
143
|
|
|
@@ -191,7 +203,8 @@ class CudaFunctions(object):
|
|
|
191
203
|
#measure power usage until kernel is done
|
|
192
204
|
t0 = time.time()
|
|
193
205
|
while not end.query():
|
|
194
|
-
|
|
206
|
+
if self.use_nvml:
|
|
207
|
+
power_readings.append([time.time()-t0, self.nvml.pwr_usage()])
|
|
195
208
|
end.synchronize()
|
|
196
209
|
execution_time = end.time_since(start) #ms
|
|
197
210
|
result["times"].append(execution_time)
|
|
@@ -233,6 +246,10 @@ class CudaFunctions(object):
|
|
|
233
246
|
logging.debug(v.flags)
|
|
234
247
|
drv.memcpy_htod(symbol, v)
|
|
235
248
|
|
|
249
|
+
def copy_shared_memory_args(self, smem_args):
|
|
250
|
+
"""add shared memory arguments to the kernel"""
|
|
251
|
+
self.smem_size = smem_args["size"]
|
|
252
|
+
|
|
236
253
|
def copy_texture_memory_args(self, texmem_args):
|
|
237
254
|
"""adds texture memory arguments to the most recently compiled module
|
|
238
255
|
|
|
@@ -308,7 +325,7 @@ class CudaFunctions(object):
|
|
|
308
325
|
of the grid
|
|
309
326
|
:type grid: tuple(int, int)
|
|
310
327
|
"""
|
|
311
|
-
func(*gpu_args, block=threads, grid=grid, stream=stream, texrefs=self.texrefs)
|
|
328
|
+
func(*gpu_args, block=threads, grid=grid, stream=stream, shared=self.smem_size, texrefs=self.texrefs)
|
|
312
329
|
|
|
313
330
|
def memset(self, allocation, value, size):
|
|
314
331
|
"""set the memory in allocation to the value in value
|
|
@@ -80,13 +80,12 @@ _kernel_options = Options([
|
|
|
80
80
|
parameters. This function should take one positional argument,
|
|
81
81
|
which will be used to pass a dict containing the parameters.
|
|
82
82
|
The function should return a string with the source code for
|
|
83
|
-
the kernel.""",
|
|
84
|
-
|
|
85
|
-
("lang", ("""Specifies the language used for GPU kernels. The kernel_tuner
|
|
83
|
+
the kernel.""", "string or list and/or callable")),
|
|
84
|
+
("lang", ("""Specifies the language used for GPU kernels. The kernel_tuner
|
|
86
85
|
automatically detects the language, but if it fails, you may specify
|
|
87
86
|
the language using this argument, currently supported: "CUDA",
|
|
88
87
|
"OpenCL", or "C".""", "string")),
|
|
89
|
-
|
|
88
|
+
("problem_size", ("""An int or string, or 1,2,3-dimensional tuple
|
|
90
89
|
containing the size from which the grid dimensions of the kernel
|
|
91
90
|
will be computed.
|
|
92
91
|
|
|
@@ -129,6 +128,12 @@ _kernel_options = Options([
|
|
|
129
128
|
the grid dimensions in the z-direction, ["block_size_z"] by default.
|
|
130
129
|
If you do not want to divide the problem_size, you should pass an empty list.
|
|
131
130
|
See grid_div_x for more details.""", "list")),
|
|
131
|
+
("smem_args", ("""CUDA-specific feature for specifying shared memory options
|
|
132
|
+
to the kernel. At the moment only 'size' is supported, but setting the
|
|
133
|
+
shared memory configuration on Kepler GPUs for example could be added
|
|
134
|
+
in the future. Size should denote the number of bytes for to use when
|
|
135
|
+
dynamically allocating shared memory.""",
|
|
136
|
+
"dict(string: numpy object)")),
|
|
132
137
|
("cmem_args", ("""CUDA-specific feature for specifying constant memory
|
|
133
138
|
arguments to the kernel. In OpenCL these are handled as normal
|
|
134
139
|
kernel arguments, but in CUDA you can copy to a symbol. The way you
|
|
@@ -142,7 +147,8 @@ _kernel_options = Options([
|
|
|
142
147
|
the texture contents. These contents can be either simply a numpy object,
|
|
143
148
|
or a dictionary containing the numpy object under the key 'array' plus the
|
|
144
149
|
configuration options 'filter_mode' ('point' or 'linear), 'address_mode'
|
|
145
|
-
(a list of 'border', 'clamp', 'mirror', 'wrap' per axis),
|
|
150
|
+
(a list of 'border', 'clamp', 'mirror', 'wrap' per axis),
|
|
151
|
+
'normalized_coordinates' (True/False).""",
|
|
146
152
|
"dict(string: numpy object or dict)")),
|
|
147
153
|
("block_size_names", ("""A list of strings that replace the defaults for the names
|
|
148
154
|
that denote the thread block dimensions. If not passed, the behavior
|
|
@@ -309,10 +315,11 @@ _tuning_options = Options([
|
|
|
309
315
|
* too many resources requested for launch
|
|
310
316
|
|
|
311
317
|
verbose is False by default.""", "bool")),
|
|
312
|
-
("cache",("""filename for caching/logging benchmarked instances
|
|
318
|
+
("cache", ("""filename for caching/logging benchmarked instances
|
|
313
319
|
filename uses suffix ".json"
|
|
314
320
|
if the file exists it is read and tuning continues from this file
|
|
315
|
-
""", "string"))
|
|
321
|
+
""", "string")),
|
|
322
|
+
("metrics", ("specifies user-defined metrics", "OrderedDict"))
|
|
316
323
|
])
|
|
317
324
|
|
|
318
325
|
_device_options = Options([
|
|
@@ -357,10 +364,10 @@ _tune_kernel_docstring = """ Tune a CUDA kernel given a set of tunable parameter
|
|
|
357
364
|
def tune_kernel(kernel_name, kernel_string, problem_size, arguments,
|
|
358
365
|
tune_params, grid_div_x=None, grid_div_y=None, grid_div_z=None,
|
|
359
366
|
restrictions=None, answer=None, atol=1e-6, verify=None, verbose=False,
|
|
360
|
-
lang=None, device=0, platform=0, cmem_args=None, texmem_args=None,
|
|
367
|
+
lang=None, device=0, platform=0, smem_args=None, cmem_args=None, texmem_args=None,
|
|
361
368
|
compiler=None, compiler_options=None, log=None,
|
|
362
369
|
iterations=7, block_size_names=None, quiet=False, strategy=None, strategy_options=None,
|
|
363
|
-
cache=None):
|
|
370
|
+
cache=None, metrics=None):
|
|
364
371
|
|
|
365
372
|
if log:
|
|
366
373
|
logging.basicConfig(filename=kernel_name + datetime.now().strftime('%Y%m%d-%H:%M:%S') + '.log', level=log)
|
|
@@ -444,7 +451,8 @@ def tune_kernel(kernel_name, kernel_string, problem_size, arguments,
|
|
|
444
451
|
if results: #checks if results is not empty
|
|
445
452
|
best_config = min(results, key=lambda x: x['time'])
|
|
446
453
|
units = getattr(runner, "units", None)
|
|
447
|
-
print("best performing configuration:"
|
|
454
|
+
print("best performing configuration:")
|
|
455
|
+
util.print_config_output(tune_params, best_config, device_options.quiet, metrics, units)
|
|
448
456
|
else:
|
|
449
457
|
print("no results to report")
|
|
450
458
|
|
|
@@ -490,7 +498,7 @@ _run_kernel_docstring = """Compile and run a single kernel
|
|
|
490
498
|
|
|
491
499
|
def run_kernel(kernel_name, kernel_string, problem_size, arguments,
|
|
492
500
|
params, grid_div_x=None, grid_div_y=None, grid_div_z=None,
|
|
493
|
-
lang=None, device=0, platform=0, cmem_args=None, texmem_args=None, compiler=None, compiler_options=None,
|
|
501
|
+
lang=None, device=0, platform=0, smem_args=None, cmem_args=None, texmem_args=None, compiler=None, compiler_options=None,
|
|
494
502
|
block_size_names=None, quiet=False, log=None):
|
|
495
503
|
|
|
496
504
|
if log:
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from kernel_tuner import core
|
|
4
|
+
from kernel_tuner.interface import Options, _kernel_options
|
|
5
|
+
|
|
6
|
+
class PythonKernel(object):
|
|
7
|
+
|
|
8
|
+
def __init__(self, kernel_name, kernel_string, problem_size, arguments, params, inputs=None, outputs=None, device=0, platform=0,
|
|
9
|
+
block_size_names=None, grid_div_x=None, grid_div_y=None, grid_div_z=None, verbose=True, lang=None):
|
|
10
|
+
""" Construct Python helper object to compile and call the kernel from Python
|
|
11
|
+
|
|
12
|
+
This object compiles a GPU kernel parameterized using the parameters in params.
|
|
13
|
+
GPU memory is allocated for each argument using its size and type as listed in arguments.
|
|
14
|
+
The object can be called directly as a function with the kernel arguments as function arguments.
|
|
15
|
+
Kernel arguments marked as inputs will be copied to the GPU on every kernel launch.
|
|
16
|
+
Only the kernel arguments marked as outputs will be returned, note that the result is always
|
|
17
|
+
returned in a list, even when there is only one output.
|
|
18
|
+
|
|
19
|
+
Most of the arguments to this function are the same as with tune_kernel or run_kernel in Kernel Tuner,
|
|
20
|
+
and are therefore not duplicated here. The two new arguments are:
|
|
21
|
+
|
|
22
|
+
:param inputs: a boolean list of length arguments to signal whether an argument is input to the kernel
|
|
23
|
+
:type inputs: list(bool)
|
|
24
|
+
|
|
25
|
+
:param outputs: a boolean list of length arguments to signal whether an argument is output of the kernel
|
|
26
|
+
:type outputs: list(bool)
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
#construct device interface
|
|
30
|
+
kernel_source = core.KernelSource(kernel_string, lang)
|
|
31
|
+
self.dev = core.DeviceInterface(kernel_source, device=device)
|
|
32
|
+
|
|
33
|
+
#construct kernel_options to hold information about the kernel
|
|
34
|
+
opts = locals()
|
|
35
|
+
kernel_options = Options([(k, opts[k]) for k in _kernel_options.keys() if k in opts.keys()])
|
|
36
|
+
|
|
37
|
+
#instantiate the kernel given the parameters in params
|
|
38
|
+
self.kernel_instance = self.dev.create_kernel_instance(kernel_source, kernel_options, params, verbose)
|
|
39
|
+
|
|
40
|
+
#compile the kernel
|
|
41
|
+
self.func = self.dev.compile_kernel(self.kernel_instance, verbose)
|
|
42
|
+
|
|
43
|
+
#setup GPU memory
|
|
44
|
+
self.gpu_args = self.dev.ready_argument_list(arguments)
|
|
45
|
+
if inputs:
|
|
46
|
+
self.inputs = inputs
|
|
47
|
+
else:
|
|
48
|
+
self.inputs = [True for _ in arguments]
|
|
49
|
+
if outputs:
|
|
50
|
+
self.outputs = outputs
|
|
51
|
+
else:
|
|
52
|
+
self.outputs = [True for _ in arguments]
|
|
53
|
+
|
|
54
|
+
def update_gpu_args(self, args):
|
|
55
|
+
for i, arg in enumerate(args):
|
|
56
|
+
if self.inputs[i]:
|
|
57
|
+
if isinstance(args[i], np.ndarray):
|
|
58
|
+
self.dev.dev.memcpy_htod(self.gpu_args[i], arg)
|
|
59
|
+
else:
|
|
60
|
+
self.gpu_args[i] = arg
|
|
61
|
+
return self.gpu_args
|
|
62
|
+
|
|
63
|
+
def get_gpu_result(self, args):
|
|
64
|
+
results = []
|
|
65
|
+
for i, _ in enumerate(self.gpu_args):
|
|
66
|
+
if self.outputs[i] and isinstance(args[i], np.ndarray):
|
|
67
|
+
res = np.zeros_like(args[i])
|
|
68
|
+
self.dev.memcpy_dtoh(res, self.gpu_args[i])
|
|
69
|
+
results.append(res)
|
|
70
|
+
return results
|
|
71
|
+
|
|
72
|
+
def run_kernel(self, args):
|
|
73
|
+
"""Run the GPU kernel
|
|
74
|
+
|
|
75
|
+
Copy the arguments marked as inputs to the GPU
|
|
76
|
+
Call the GPU kernel
|
|
77
|
+
Copy the arguments marked as outputs from the GPU
|
|
78
|
+
Return the outputs in a list of numpy arrays
|
|
79
|
+
|
|
80
|
+
:param args: A list with the kernel arguments as numpy arrays or numpy scalars
|
|
81
|
+
:type args: list(np.ndarray or np.generic)
|
|
82
|
+
"""
|
|
83
|
+
self.update_gpu_args(args)
|
|
84
|
+
self.dev.run_kernel(self.func, self.gpu_args, self.kernel_instance)
|
|
85
|
+
return self.get_gpu_result(args)
|
|
86
|
+
|
|
87
|
+
def __call__(self, *args):
|
|
88
|
+
"""Run the GPU kernel
|
|
89
|
+
|
|
90
|
+
Copy the arguments marked as inputs to the GPU
|
|
91
|
+
Call the GPU kernel
|
|
92
|
+
Copy the arguments marked as outputs from the GPU
|
|
93
|
+
Return the outputs in a list of numpy arrays
|
|
94
|
+
|
|
95
|
+
:param *args: A variable number of kernel arguments as numpy arrays or numpy scalars
|
|
96
|
+
:type *args: np.ndarray or np.generic
|
|
97
|
+
"""
|
|
98
|
+
return self.run_kernel(args)
|
|
@@ -69,7 +69,7 @@ class OpenCLFunctions(object):
|
|
|
69
69
|
gpu_args.append(arg)
|
|
70
70
|
return gpu_args
|
|
71
71
|
|
|
72
|
-
def compile(self,
|
|
72
|
+
def compile(self, kernel_instance):
|
|
73
73
|
"""call the OpenCL compiler to compile the kernel, return the device function
|
|
74
74
|
|
|
75
75
|
:param kernel_name: The name of the kernel to be compiled, used to lookup the
|
|
@@ -82,8 +82,8 @@ class OpenCLFunctions(object):
|
|
|
82
82
|
:returns: An OpenCL kernel that can be called directly.
|
|
83
83
|
:rtype: pyopencl.Kernel
|
|
84
84
|
"""
|
|
85
|
-
prg = cl.Program(self.ctx, kernel_string).build(options=self.compiler_options)
|
|
86
|
-
func = getattr(prg,
|
|
85
|
+
prg = cl.Program(self.ctx, kernel_instance.kernel_string).build(options=self.compiler_options)
|
|
86
|
+
func = getattr(prg, kernel_instance.name)
|
|
87
87
|
return func
|
|
88
88
|
|
|
89
89
|
def benchmark(self, func, gpu_args, threads, grid):
|
|
@@ -4,7 +4,7 @@ from __future__ import print_function
|
|
|
4
4
|
from collections import OrderedDict
|
|
5
5
|
import logging
|
|
6
6
|
|
|
7
|
-
from kernel_tuner.util import get_config_string, store_cache
|
|
7
|
+
from kernel_tuner.util import get_config_string, store_cache, process_metrics, print_config_output
|
|
8
8
|
from kernel_tuner.core import DeviceInterface
|
|
9
9
|
|
|
10
10
|
|
|
@@ -35,6 +35,8 @@ class SequentialRunner(object):
|
|
|
35
35
|
self.quiet = device_options.quiet
|
|
36
36
|
self.kernel_source = kernel_source
|
|
37
37
|
|
|
38
|
+
self.warmed_up = False
|
|
39
|
+
|
|
38
40
|
#move data to the GPU
|
|
39
41
|
self.gpu_args = self.dev.ready_argument_list(kernel_options.arguments)
|
|
40
42
|
|
|
@@ -66,6 +68,11 @@ class SequentialRunner(object):
|
|
|
66
68
|
for element in parameter_space:
|
|
67
69
|
params = OrderedDict(zip(tuning_options.tune_params.keys(), element))
|
|
68
70
|
|
|
71
|
+
#attempt to warmup the GPU by running the first config in the parameter space and ignoring the result
|
|
72
|
+
if not self.warmed_up:
|
|
73
|
+
self.dev.compile_and_benchmark(self.kernel_source, self.gpu_args, params, kernel_options, tuning_options)
|
|
74
|
+
self.warmed_up = True
|
|
75
|
+
|
|
69
76
|
#check if element is in the cache
|
|
70
77
|
x_int = ",".join([str(i) for i in element])
|
|
71
78
|
if tuning_options.cache:
|
|
@@ -87,14 +94,16 @@ class SequentialRunner(object):
|
|
|
87
94
|
time = result
|
|
88
95
|
|
|
89
96
|
params['time'] = time
|
|
90
|
-
output_string = get_config_string(params, params.keys(), self.units)
|
|
91
|
-
logging.debug(output_string)
|
|
92
|
-
if not self.quiet:
|
|
93
|
-
print(output_string)
|
|
94
97
|
|
|
95
98
|
if isinstance(result, dict):
|
|
96
99
|
params.update(result)
|
|
97
100
|
|
|
101
|
+
|
|
102
|
+
if tuning_options.metrics:
|
|
103
|
+
params = process_metrics(params, tuning_options.metrics)
|
|
104
|
+
|
|
105
|
+
print_config_output(tuning_options.tune_params, params, self.quiet, tuning_options.metrics, self.units)
|
|
106
|
+
|
|
98
107
|
store_cache(x_int, params, tuning_options)
|
|
99
108
|
results.append(params)
|
|
100
109
|
|
|
@@ -104,3 +113,7 @@ class SequentialRunner(object):
|
|
|
104
113
|
def __del__(self):
|
|
105
114
|
if hasattr(self, 'dev'):
|
|
106
115
|
del self.dev
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
from __future__ import print_function
|
|
3
3
|
|
|
4
4
|
from collections import OrderedDict
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from bayes_opt import BayesianOptimization
|
|
8
|
+
bayes_opt_present=True
|
|
9
|
+
except Exception:
|
|
10
|
+
BayesianOptimization = None
|
|
11
|
+
bayes_opt_present=False
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
|
|
7
15
|
from kernel_tuner.strategies import minimize
|
|
8
16
|
|
|
@@ -47,6 +55,9 @@ def tune(runner, kernel_options, device_options, tuning_options):
|
|
|
47
55
|
# acq="ei", xi=1e-1
|
|
48
56
|
# acq="ucb", kappa=10
|
|
49
57
|
|
|
58
|
+
if not bayes_opt_present:
|
|
59
|
+
raise ImportError("Error: optional dependency Bayesian Optimization not installed")
|
|
60
|
+
|
|
50
61
|
#defaults as used by Bayesian Optimization Python package
|
|
51
62
|
acq = tuning_options.strategy_options.get("method", "poi")
|
|
52
63
|
kappa = tuning_options.strategy_options.get("kappa", 2.576)
|
|
@@ -98,19 +98,28 @@ def _cost_func(x, kernel_options, tuning_options, runner, results):
|
|
|
98
98
|
|
|
99
99
|
def get_bounds_x0_eps(tuning_options):
|
|
100
100
|
"""compute bounds, x0 (the initial guess), and eps"""
|
|
101
|
-
values = tuning_options.tune_params.values()
|
|
101
|
+
values = list(tuning_options.tune_params.values())
|
|
102
|
+
|
|
103
|
+
if "x0" in tuning_options.strategy_options:
|
|
104
|
+
x0 = tuning_options.strategy_options.x0
|
|
105
|
+
else:
|
|
106
|
+
x0 = None
|
|
102
107
|
|
|
103
108
|
if tuning_options.scaling:
|
|
104
|
-
#bounds = [(0, 1) for _ in values]
|
|
105
|
-
#x0 = [0.5 for _ in bounds]
|
|
106
109
|
eps = numpy.amin([1.0/len(v) for v in values])
|
|
107
110
|
|
|
108
111
|
#reducing interval from [0, 1] to [0, eps*len(v)]
|
|
109
112
|
bounds = [(0, eps*len(v)) for v in values]
|
|
110
|
-
x0
|
|
113
|
+
if x0:
|
|
114
|
+
#x0 has been supplied by the user, map x0 into [0, eps*len(v)]
|
|
115
|
+
for i, e in enumerate(values):
|
|
116
|
+
x0[i] = eps*values[i].index(x0[i])
|
|
117
|
+
else:
|
|
118
|
+
x0 = [0.5*eps*len(v) for v in values]
|
|
111
119
|
else:
|
|
112
120
|
bounds = get_bounds(tuning_options.tune_params)
|
|
113
|
-
|
|
121
|
+
if not x0:
|
|
122
|
+
x0 = [(min_v+max_v)/2.0 for (min_v, max_v) in bounds]
|
|
114
123
|
eps = 1e9
|
|
115
124
|
for v_list in values:
|
|
116
125
|
vals = numpy.sort(v_list)
|