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