kernel-tuner 0.3.0__tar.gz → 0.3.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.3.0 → kernel_tuner-0.3.1}/PKG-INFO +4 -4
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/c.py +20 -12
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/core.py +4 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/cuda.py +21 -4
- kernel_tuner-0.3.1/kernel_tuner/cupy_backend.py +331 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/interface.py +14 -8
- kernel_tuner-0.3.1/kernel_tuner/kernelbuilder.py +98 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/opencl.py +3 -3
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/bayes_opt.py +12 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/util.py +0 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner.egg-info/PKG-INFO +4 -4
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner.egg-info/SOURCES.txt +2 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/setup.py +1 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/test/test_c_functions.py +34 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/test/test_cuda_functions.py +9 -3
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/test/test_cuda_mocked.py +5 -2
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/test/test_interface.py +3 -3
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/test/test_opencl_functions.py +6 -1
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/test/test_util_functions.py +1 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/LICENSE +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/MANIFEST.in +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/README.rst +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/__init__.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/nvml.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/runners/__init__.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/runners/sequential.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/__init__.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/basinhopping.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/brute_force.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/diff_evo.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/firefly_algorithm.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/genetic_algorithm.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/minimize.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/pso.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/random_sample.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/strategies/simulated_annealing.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner/wrappers.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner.egg-info/dependency_links.txt +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner.egg-info/requires.txt +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/kernel_tuner.egg-info/top_level.txt +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/setup.cfg +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/test/test_core.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/test/test_minimize.py +0 -0
- {kernel_tuner-0.3.0 → kernel_tuner-0.3.1}/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.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
|
|
@@ -202,8 +202,8 @@ Classifier: Topic :: Software Development
|
|
|
202
202
|
Classifier: Topic :: System :: Distributed Computing
|
|
203
203
|
Classifier: Development Status :: 4 - Beta
|
|
204
204
|
Provides-Extra: dev
|
|
205
|
-
Provides-Extra: doc
|
|
206
|
-
Provides-Extra: cuda_opencl
|
|
207
|
-
Provides-Extra: tutorial
|
|
208
205
|
Provides-Extra: cuda
|
|
206
|
+
Provides-Extra: tutorial
|
|
209
207
|
Provides-Extra: opencl
|
|
208
|
+
Provides-Extra: cuda_opencl
|
|
209
|
+
Provides-Extra: doc
|
|
@@ -105,7 +105,7 @@ class CFunctions(object):
|
|
|
105
105
|
ctype_args[i] = Argument(numpy=data, ctypes=data_ctypes)
|
|
106
106
|
return ctype_args
|
|
107
107
|
|
|
108
|
-
def compile(self,
|
|
108
|
+
def compile(self, kernel_instance):
|
|
109
109
|
"""call the C compiler to compile the kernel, return the function
|
|
110
110
|
|
|
111
111
|
:param kernel_name: The name of the kernel to be compiled, used to lookup the
|
|
@@ -118,7 +118,9 @@ class CFunctions(object):
|
|
|
118
118
|
:returns: An ctypes function that can be called directly.
|
|
119
119
|
:rtype: ctypes._FuncPtr
|
|
120
120
|
"""
|
|
121
|
-
logging.debug('compiling ' +
|
|
121
|
+
logging.debug('compiling ' + kernel_instance.name)
|
|
122
|
+
|
|
123
|
+
kernel_string = kernel_instance.kernel_string
|
|
122
124
|
|
|
123
125
|
if self.lib != None:
|
|
124
126
|
self.cleanup_lib()
|
|
@@ -134,25 +136,31 @@ class CFunctions(object):
|
|
|
134
136
|
else:
|
|
135
137
|
compiler_options.append("-fopenmp")
|
|
136
138
|
|
|
137
|
-
#
|
|
139
|
+
#if filename is known, use that one
|
|
140
|
+
suffix = kernel_instance.kernel_source.get_user_suffix()
|
|
138
141
|
|
|
139
|
-
#
|
|
140
|
-
|
|
142
|
+
#if code contains device code, suffix .cu is required
|
|
143
|
+
device_code_signals = ["__global", "__syncthreads()", "threadIdx"]
|
|
144
|
+
if any([snippet in kernel_string for snippet in device_code_signals]):
|
|
145
|
+
suffix = ".cu"
|
|
141
146
|
|
|
142
147
|
#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):
|
|
148
|
+
if (suffix == ".cu") or ("#include <cuda" in kernel_string) or ("cudaMemcpy" in kernel_string):
|
|
144
149
|
if self.compiler == "g++" and self.nvcc_available:
|
|
145
150
|
self.compiler = "nvcc"
|
|
146
151
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
suffix = ".
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
if suffix is None:
|
|
153
|
+
#select right suffix based on compiler
|
|
154
|
+
suffix = ".cc"
|
|
155
|
+
|
|
156
|
+
if self.compiler in ["gfortran", "pgfortran", "ftn", "ifort"]:
|
|
157
|
+
suffix = ".F90"
|
|
152
158
|
|
|
153
159
|
if self.compiler == "nvcc":
|
|
154
160
|
compiler_options = ["-Xcompiler=" + c for c in compiler_options]
|
|
155
161
|
|
|
162
|
+
#this basically checks if we aren't compiling Fortran
|
|
163
|
+
#at the moment any C, C++, or CUDA code is assumed to use extern "C" linkage
|
|
156
164
|
if ".c" in suffix:
|
|
157
165
|
if not "extern \"C\"" in kernel_string:
|
|
158
166
|
kernel_string = "extern \"C\" {\n" + kernel_string + "\n}"
|
|
@@ -194,7 +202,7 @@ class CFunctions(object):
|
|
|
194
202
|
|
|
195
203
|
|
|
196
204
|
self.lib = numpy.ctypeslib.load_library(filename, '.')
|
|
197
|
-
func = getattr(self.lib,
|
|
205
|
+
func = getattr(self.lib, kernel_instance.name)
|
|
198
206
|
func.restype = C.c_float
|
|
199
207
|
|
|
200
208
|
finally:
|
|
@@ -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,7 +344,7 @@ 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
|
|
@@ -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
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"""This module contains all CUDA specific kernel_tuner functions"""
|
|
2
|
+
from __future__ import print_function
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import time
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
import cupy as cp
|
|
9
|
+
|
|
10
|
+
import cupy.cuda as cuda
|
|
11
|
+
|
|
12
|
+
from kernel_tuner.nvml import nvml
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CupyBackend(object):
|
|
18
|
+
"""Class that groups the CUDA functions on maintains state about the device"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, device=0, iterations=7, compiler_options=None):
|
|
21
|
+
"""instantiate CudaFunctions object used for interacting with the CUDA device
|
|
22
|
+
|
|
23
|
+
Instantiating this object will inspect and store certain device properties at
|
|
24
|
+
runtime, which are used during compilation and/or execution of kernels by the
|
|
25
|
+
kernel tuner. It also maintains a reference to the most recently compiled
|
|
26
|
+
source module for copying data to constant memory before kernel launch.
|
|
27
|
+
|
|
28
|
+
:param device: Number of CUDA device to use for this context
|
|
29
|
+
:type device: int
|
|
30
|
+
|
|
31
|
+
:param iterations: Number of iterations used while benchmarking a kernel, 7 by default.
|
|
32
|
+
:type iterations: int
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
self.allocations = []
|
|
36
|
+
self.compiler_options = compiler_options
|
|
37
|
+
self.iterations = iterations
|
|
38
|
+
|
|
39
|
+
self.dev = cuda.Device(device)
|
|
40
|
+
self.dev.use() #make this the current device
|
|
41
|
+
self.smem_size = 0
|
|
42
|
+
self.use_nvml = False
|
|
43
|
+
|
|
44
|
+
self.stream = cuda.Stream()
|
|
45
|
+
self.max_threads = self.dev.attributes["MaxThreadsPerBlock"]
|
|
46
|
+
self.cc = self.dev.compute_capability
|
|
47
|
+
|
|
48
|
+
self.env = {"attributes": self.dev.attributes}
|
|
49
|
+
self.name = "cupy does not name device"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def ready_argument_list(self, arguments):
|
|
54
|
+
"""ready argument list to be passed to the kernel, allocates gpu mem
|
|
55
|
+
|
|
56
|
+
:param arguments: List of arguments to be passed to the kernel.
|
|
57
|
+
The order should match the argument list on the CUDA kernel.
|
|
58
|
+
Allowed values are np.ndarray, and/or np.int32, np.float32, and so on.
|
|
59
|
+
:type arguments: list(numpy objects)
|
|
60
|
+
|
|
61
|
+
:returns: A list of arguments that can be passed to an CUDA kernel.
|
|
62
|
+
:rtype: list( pycuda.driver.DeviceAllocation, np.int32, ... )
|
|
63
|
+
"""
|
|
64
|
+
gpu_args = []
|
|
65
|
+
for arg in arguments:
|
|
66
|
+
# if arg i is a numpy array copy to device
|
|
67
|
+
if isinstance(arg, np.ndarray):
|
|
68
|
+
alloc = cp.asarray(arg)
|
|
69
|
+
self.allocations.append(alloc)
|
|
70
|
+
gpu_args.append(alloc)
|
|
71
|
+
else: # if not an array, just pass argument along
|
|
72
|
+
#if we remove other checks this might just pass along cupy arrays too
|
|
73
|
+
gpu_args.append(arg)
|
|
74
|
+
return gpu_args
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def compile(self, kernel_instance):
|
|
78
|
+
"""call the CUDA compiler to compile the kernel, return the device function
|
|
79
|
+
|
|
80
|
+
:param kernel_name: The name of the kernel to be compiled, used to lookup the
|
|
81
|
+
function after compilation.
|
|
82
|
+
:type kernel_name: string
|
|
83
|
+
|
|
84
|
+
:param kernel_string: The CUDA kernel code that contains the function `kernel_name`
|
|
85
|
+
:type kernel_string: string
|
|
86
|
+
|
|
87
|
+
:returns: An CUDA kernel that can be called directly.
|
|
88
|
+
:rtype: pycuda.driver.Function
|
|
89
|
+
"""
|
|
90
|
+
kernel_string = kernel_instance.kernel_string
|
|
91
|
+
kernel_name = kernel_instance.name
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
#mimic pycuda behavior for the moment, this can be removed once
|
|
95
|
+
#https://github.com/cupy/cupy/pull/3319
|
|
96
|
+
#has been merged
|
|
97
|
+
no_extern_c = not 'extern "C"' in kernel_string
|
|
98
|
+
if no_extern_c:
|
|
99
|
+
kernel_string = 'extern "C" {\n' + kernel_string + '\n}\n'
|
|
100
|
+
|
|
101
|
+
#self.current_module = cp.RawModule(code=kernel_string, options=self.compiler_options)
|
|
102
|
+
self.current_module = cp.RawModule(code=kernel_string)
|
|
103
|
+
|
|
104
|
+
func = self.current_module.get_function(kernel_name)
|
|
105
|
+
return func
|
|
106
|
+
except Exception as e:
|
|
107
|
+
if "uses too much shared data" in e.stderr:
|
|
108
|
+
raise Exception("uses too much shared data")
|
|
109
|
+
else:
|
|
110
|
+
raise e
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def benchmark(self, func, gpu_args, threads, grid):
|
|
114
|
+
"""runs the kernel and measures time repeatedly, returns average time
|
|
115
|
+
|
|
116
|
+
Runs the kernel and measures kernel execution time repeatedly, number of
|
|
117
|
+
iterations is set during the creation of CudaFunctions. Benchmark returns
|
|
118
|
+
a robust average, from all measurements the fastest and slowest runs are
|
|
119
|
+
discarded and the rest is included in the returned average. The reason for
|
|
120
|
+
this is to be robust against initialization artifacts and other exceptional
|
|
121
|
+
cases.
|
|
122
|
+
|
|
123
|
+
:param func: A PyCuda kernel compiled for this specific kernel configuration
|
|
124
|
+
:type func: pycuda.driver.Function
|
|
125
|
+
|
|
126
|
+
:param gpu_args: A list of arguments to the kernel, order should match the
|
|
127
|
+
order in the code. Allowed values are either variables in global memory
|
|
128
|
+
or single values passed by value.
|
|
129
|
+
:type gpu_args: list( pycuda.driver.DeviceAllocation, np.int32, ...)
|
|
130
|
+
|
|
131
|
+
:param threads: A tuple listing the number of threads in each dimension of
|
|
132
|
+
the thread block
|
|
133
|
+
:type threads: tuple(int, int, int)
|
|
134
|
+
|
|
135
|
+
:param grid: A tuple listing the number of thread blocks in each dimension
|
|
136
|
+
of the grid
|
|
137
|
+
:type grid: tuple(int, int)
|
|
138
|
+
|
|
139
|
+
:returns: A dictionary with benchmark results.
|
|
140
|
+
:rtype: dict()
|
|
141
|
+
"""
|
|
142
|
+
result = dict()
|
|
143
|
+
result["times"] = []
|
|
144
|
+
result["power"] = []
|
|
145
|
+
energy = []
|
|
146
|
+
start = cuda.Event()
|
|
147
|
+
end = cuda.Event()
|
|
148
|
+
self.dev.synchronize()
|
|
149
|
+
for _ in range(self.iterations):
|
|
150
|
+
power_readings = []
|
|
151
|
+
self.stream.record(start)
|
|
152
|
+
self.run_kernel(func, gpu_args, threads, grid, stream=self.stream)
|
|
153
|
+
self.stream.record(end)
|
|
154
|
+
#measure power usage until kernel is done
|
|
155
|
+
t0 = time.time()
|
|
156
|
+
while not end.done:
|
|
157
|
+
if self.use_nvml:
|
|
158
|
+
power_readings.append([time.time()-t0, self.nvml.pwr_usage()])
|
|
159
|
+
end.synchronize()
|
|
160
|
+
execution_time = cuda.get_elapsed_time(start, end) #ms
|
|
161
|
+
result["times"].append(execution_time)
|
|
162
|
+
|
|
163
|
+
#pre and postfix to start at 0 and end at kernel end
|
|
164
|
+
if power_readings:
|
|
165
|
+
power_readings = [[0.0, power_readings[0][1]]] + power_readings
|
|
166
|
+
power_readings = power_readings + [[execution_time / 1000.0, power_readings[-1][1]]]
|
|
167
|
+
result["power"].append(power_readings) #time in s, power usage in milliwatts
|
|
168
|
+
|
|
169
|
+
#compute energy consumption as area under curve
|
|
170
|
+
x = [d[0] for d in power_readings]
|
|
171
|
+
y = [d[1]/1000.0 for d in power_readings] #convert to watts
|
|
172
|
+
energy.append(np.trapz(y,x)) #in Joule
|
|
173
|
+
|
|
174
|
+
result["time"] = np.mean(result["times"])
|
|
175
|
+
if (self.iterations > 10) and energy:
|
|
176
|
+
result["energy"] = np.mean(energy[10:])
|
|
177
|
+
return result
|
|
178
|
+
|
|
179
|
+
def copy_constant_memory_args(self, cmem_args):
|
|
180
|
+
"""adds constant memory arguments to the most recently compiled module
|
|
181
|
+
|
|
182
|
+
:param cmem_args: A dictionary containing the data to be passed to the
|
|
183
|
+
device constant memory. The format to be used is as follows: A
|
|
184
|
+
string key is used to name the constant memory symbol to which the
|
|
185
|
+
value needs to be copied. Similar to regular arguments, these need
|
|
186
|
+
to be numpy objects, such as np.ndarray or np.int32, and so on.
|
|
187
|
+
:type cmem_args: dict( string: np.ndarray, ... )
|
|
188
|
+
"""
|
|
189
|
+
logging.debug('copy_constant_memory_args called')
|
|
190
|
+
logging.debug('current module: ' + str(self.current_module))
|
|
191
|
+
for k, v in cmem_args.items():
|
|
192
|
+
#symbol = self.current_module.get_global(k)[0]
|
|
193
|
+
logging.debug('copying to symbol: ' + str(symbol))
|
|
194
|
+
logging.debug('array to be copied: ')
|
|
195
|
+
logging.debug(v.nbytes)
|
|
196
|
+
logging.debug(v.dtype)
|
|
197
|
+
logging.debug(v.flags)
|
|
198
|
+
#drv.memcpy_htod(symbol, v)
|
|
199
|
+
|
|
200
|
+
def copy_shared_memory_args(self, smem_args):
|
|
201
|
+
"""add shared memory arguments to the kernel"""
|
|
202
|
+
self.smem_size = smem_args["size"]
|
|
203
|
+
|
|
204
|
+
def copy_texture_memory_args(self, texmem_args):
|
|
205
|
+
"""adds texture memory arguments to the most recently compiled module
|
|
206
|
+
|
|
207
|
+
:param texmem_args: A dictionary containing the data to be passed to the
|
|
208
|
+
device texture memory. See tune_kernel().
|
|
209
|
+
:type texmem_args: dict
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
"""
|
|
213
|
+
filter_mode_map = { 'point': drv.filter_mode.POINT,
|
|
214
|
+
'linear': drv.filter_mode.LINEAR }
|
|
215
|
+
address_mode_map = { 'border': drv.address_mode.BORDER,
|
|
216
|
+
'clamp': drv.address_mode.CLAMP,
|
|
217
|
+
'mirror': drv.address_mode.MIRROR,
|
|
218
|
+
'wrap': drv.address_mode.WRAP }
|
|
219
|
+
|
|
220
|
+
logging.debug('copy_texture_memory_args called')
|
|
221
|
+
logging.debug('current module: ' + str(self.current_module))
|
|
222
|
+
self.texrefs = []
|
|
223
|
+
for k, v in texmem_args.items():
|
|
224
|
+
tex = self.current_module.get_texref(k)
|
|
225
|
+
self.texrefs.append(tex)
|
|
226
|
+
|
|
227
|
+
logging.debug('copying to texture: ' + str(k))
|
|
228
|
+
if not isinstance(v, dict):
|
|
229
|
+
data = v
|
|
230
|
+
else:
|
|
231
|
+
data = v['array']
|
|
232
|
+
logging.debug('texture to be copied: ')
|
|
233
|
+
logging.debug(data.nbytes)
|
|
234
|
+
logging.debug(data.dtype)
|
|
235
|
+
logging.debug(data.flags)
|
|
236
|
+
|
|
237
|
+
drv.matrix_to_texref(data, tex, order="C")
|
|
238
|
+
|
|
239
|
+
if isinstance(v, dict):
|
|
240
|
+
if 'address_mode' in v and v['address_mode'] is not None:
|
|
241
|
+
# address_mode is set per axis
|
|
242
|
+
amode = v['address_mode']
|
|
243
|
+
if not isinstance(amode, list):
|
|
244
|
+
amode = [ amode ] * data.ndim
|
|
245
|
+
for i, m in enumerate(amode):
|
|
246
|
+
try:
|
|
247
|
+
if m is not None:
|
|
248
|
+
tex.set_address_mode(i, address_mode_map[m])
|
|
249
|
+
except KeyError:
|
|
250
|
+
raise ValueError('Unknown address mode: ' + m)
|
|
251
|
+
if 'filter_mode' in v and v['filter_mode'] is not None:
|
|
252
|
+
fmode = v['filter_mode']
|
|
253
|
+
try:
|
|
254
|
+
tex.set_filter_mode(filter_mode_map[fmode])
|
|
255
|
+
except KeyError:
|
|
256
|
+
raise ValueError('Unknown filter mode: ' + fmode)
|
|
257
|
+
if 'normalized_coordinates' in v and v['normalized_coordinates']:
|
|
258
|
+
tex.set_flags(tex.get_flags() | drv.TRSF_NORMALIZED_COORDINATES)
|
|
259
|
+
"""
|
|
260
|
+
pass
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def run_kernel(self, func, gpu_args, threads, grid, stream=None):
|
|
264
|
+
"""runs the CUDA kernel passed as 'func'
|
|
265
|
+
|
|
266
|
+
:param func: A PyCuda kernel compiled for this specific kernel configuration
|
|
267
|
+
:type func: pycuda.driver.Function
|
|
268
|
+
|
|
269
|
+
:param gpu_args: A list of arguments to the kernel, order should match the
|
|
270
|
+
order in the code. Allowed values are either variables in global memory
|
|
271
|
+
or single values passed by value.
|
|
272
|
+
:type gpu_args: list( pycuda.driver.DeviceAllocation, np.int32, ...)
|
|
273
|
+
|
|
274
|
+
:param threads: A tuple listing the number of threads in each dimension of
|
|
275
|
+
the thread block
|
|
276
|
+
:type threads: tuple(int, int, int)
|
|
277
|
+
|
|
278
|
+
:param grid: A tuple listing the number of thread blocks in each dimension
|
|
279
|
+
of the grid
|
|
280
|
+
:type grid: tuple(int, int)
|
|
281
|
+
"""
|
|
282
|
+
func(grid, threads, gpu_args, shared_mem=self.smem_size)
|
|
283
|
+
|
|
284
|
+
def memset(self, allocation, value, size):
|
|
285
|
+
"""set the memory in allocation to the value in value
|
|
286
|
+
|
|
287
|
+
:param allocation: A GPU memory allocation unit
|
|
288
|
+
:type allocation: pycuda.driver.DeviceAllocation
|
|
289
|
+
|
|
290
|
+
:param value: The value to set the memory to
|
|
291
|
+
:type value: a single 8-bit unsigned int
|
|
292
|
+
|
|
293
|
+
:param size: The size of to the allocation unit in bytes
|
|
294
|
+
:type size: int
|
|
295
|
+
|
|
296
|
+
"""
|
|
297
|
+
#drv.memset_d8(allocation, value, size)
|
|
298
|
+
allocation = cp.full_like(allocation, value)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def memcpy_dtoh(self, dest, src):
|
|
302
|
+
"""perform a device to host memory copy
|
|
303
|
+
|
|
304
|
+
:param dest: A numpy array in host memory to store the data
|
|
305
|
+
:type dest: np.ndarray
|
|
306
|
+
|
|
307
|
+
:param src: A GPU memory allocation unit
|
|
308
|
+
:type src: pycuda.driver.DeviceAllocation
|
|
309
|
+
"""
|
|
310
|
+
if isinstance(src, cp.ndarray):
|
|
311
|
+
dest = cp.ndarray.get()
|
|
312
|
+
else:
|
|
313
|
+
dest = src
|
|
314
|
+
|
|
315
|
+
def memcpy_htod(self, dest, src):
|
|
316
|
+
"""perform a host to device memory copy
|
|
317
|
+
|
|
318
|
+
:param dest: A GPU memory allocation unit
|
|
319
|
+
:type dest: pycuda.driver.DeviceAllocation
|
|
320
|
+
|
|
321
|
+
:param src: A numpy array in host memory to store the data
|
|
322
|
+
:type src: np.ndarray
|
|
323
|
+
"""
|
|
324
|
+
if isinstance(dest, cp.ndarray):
|
|
325
|
+
cp.copyto(dest, src) #might fail if src is not cupy array
|
|
326
|
+
else:
|
|
327
|
+
dest = src
|
|
328
|
+
|
|
329
|
+
units = {'time': 'ms',
|
|
330
|
+
'power': 's,mW',
|
|
331
|
+
'energy': 'J'}
|
|
@@ -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,7 +315,7 @@ _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
321
|
""", "string"))
|
|
@@ -357,7 +363,7 @@ _tune_kernel_docstring = """ Tune a CUDA kernel given a set of tunable parameter
|
|
|
357
363
|
def tune_kernel(kernel_name, kernel_string, problem_size, arguments,
|
|
358
364
|
tune_params, grid_div_x=None, grid_div_y=None, grid_div_z=None,
|
|
359
365
|
restrictions=None, answer=None, atol=1e-6, verify=None, verbose=False,
|
|
360
|
-
lang=None, device=0, platform=0, cmem_args=None, texmem_args=None,
|
|
366
|
+
lang=None, device=0, platform=0, smem_args=None, cmem_args=None, texmem_args=None,
|
|
361
367
|
compiler=None, compiler_options=None, log=None,
|
|
362
368
|
iterations=7, block_size_names=None, quiet=False, strategy=None, strategy_options=None,
|
|
363
369
|
cache=None):
|
|
@@ -490,7 +496,7 @@ _run_kernel_docstring = """Compile and run a single kernel
|
|
|
490
496
|
|
|
491
497
|
def run_kernel(kernel_name, kernel_string, problem_size, arguments,
|
|
492
498
|
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,
|
|
499
|
+
lang=None, device=0, platform=0, smem_args=None, cmem_args=None, texmem_args=None, compiler=None, compiler_options=None,
|
|
494
500
|
block_size_names=None, quiet=False, log=None):
|
|
495
501
|
|
|
496
502
|
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):
|
|
@@ -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)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kernel-tuner
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.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
|
|
@@ -202,8 +202,8 @@ Classifier: Topic :: Software Development
|
|
|
202
202
|
Classifier: Topic :: System :: Distributed Computing
|
|
203
203
|
Classifier: Development Status :: 4 - Beta
|
|
204
204
|
Provides-Extra: dev
|
|
205
|
-
Provides-Extra: doc
|
|
206
|
-
Provides-Extra: cuda_opencl
|
|
207
|
-
Provides-Extra: tutorial
|
|
208
205
|
Provides-Extra: cuda
|
|
206
|
+
Provides-Extra: tutorial
|
|
209
207
|
Provides-Extra: opencl
|
|
208
|
+
Provides-Extra: cuda_opencl
|
|
209
|
+
Provides-Extra: doc
|
|
@@ -10,6 +10,7 @@ except ImportError:
|
|
|
10
10
|
from unittest.mock import patch, Mock
|
|
11
11
|
|
|
12
12
|
from kernel_tuner.c import CFunctions, Argument
|
|
13
|
+
from kernel_tuner.core import KernelSource, KernelInstance
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def test_ready_argument_list1():
|
|
@@ -119,9 +120,12 @@ def test_compile(npct, subprocess):
|
|
|
119
120
|
|
|
120
121
|
kernel_string = "this is a fake C program"
|
|
121
122
|
kernel_name = "blabla"
|
|
123
|
+
kernel_sources = KernelSource(kernel_string, "C")
|
|
124
|
+
kernel_instance = KernelInstance(kernel_name, kernel_sources, kernel_string, [], None, None, dict(), [])
|
|
122
125
|
|
|
123
126
|
cfunc = CFunctions()
|
|
124
|
-
|
|
127
|
+
|
|
128
|
+
f = cfunc.compile(kernel_instance)
|
|
125
129
|
|
|
126
130
|
print(subprocess.mock_calls)
|
|
127
131
|
print(npct.mock_calls)
|
|
@@ -141,6 +145,35 @@ def test_compile(npct, subprocess):
|
|
|
141
145
|
assert not os.path.isfile(filename + ".so")
|
|
142
146
|
|
|
143
147
|
|
|
148
|
+
@patch('kernel_tuner.c.subprocess')
|
|
149
|
+
@patch('kernel_tuner.c.numpy.ctypeslib')
|
|
150
|
+
def test_compile_detects_device_code(npct, subprocess):
|
|
151
|
+
|
|
152
|
+
kernel_string = "this code clearly contains device code __global__ kernel(float* arg){ return; }"
|
|
153
|
+
kernel_name = "blabla"
|
|
154
|
+
kernel_sources = KernelSource(kernel_string, "C")
|
|
155
|
+
kernel_instance = KernelInstance(kernel_name, kernel_sources, kernel_string, [], None, None, dict(), [])
|
|
156
|
+
|
|
157
|
+
cfunc = CFunctions()
|
|
158
|
+
|
|
159
|
+
f = cfunc.compile(kernel_instance)
|
|
160
|
+
|
|
161
|
+
print(subprocess.check_call.call_args_list)
|
|
162
|
+
|
|
163
|
+
#assert the filename suffix used for source compilation is .cu
|
|
164
|
+
dot_cu_used = False
|
|
165
|
+
for call in subprocess.check_call.call_args_list:
|
|
166
|
+
args, kwargs = call
|
|
167
|
+
args = args[0]
|
|
168
|
+
print(args)
|
|
169
|
+
if args[0] == 'nvcc' and args[1] == '-c':
|
|
170
|
+
assert args[2][-3:] == '.cu'
|
|
171
|
+
dot_cu_used = True
|
|
172
|
+
|
|
173
|
+
assert dot_cu_used
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
144
177
|
def test_memset():
|
|
145
178
|
a = [1, 2, 3, 4]
|
|
146
179
|
x = numpy.array(a).astype(numpy.float32)
|
|
@@ -2,6 +2,7 @@ import numpy
|
|
|
2
2
|
from .context import skip_if_no_cuda
|
|
3
3
|
|
|
4
4
|
from kernel_tuner import cuda
|
|
5
|
+
from kernel_tuner.core import KernelSource, KernelInstance
|
|
5
6
|
|
|
6
7
|
try:
|
|
7
8
|
import pycuda.driver
|
|
@@ -39,11 +40,15 @@ def test_compile():
|
|
|
39
40
|
}
|
|
40
41
|
"""
|
|
41
42
|
|
|
43
|
+
kernel_sources = KernelSource(original_kernel, "cuda")
|
|
44
|
+
|
|
45
|
+
|
|
42
46
|
kernel_string = original_kernel.replace("shared_size", str(100*1024*1024))
|
|
47
|
+
kernel_instance = KernelInstance("vector_add", kernel_sources, kernel_string, [], None, None, dict(), [])
|
|
43
48
|
|
|
44
49
|
dev = cuda.CudaFunctions(0)
|
|
45
50
|
try:
|
|
46
|
-
func = dev.compile(
|
|
51
|
+
func = dev.compile(kernel_instance)
|
|
47
52
|
assert isinstance(func, pycuda.driver.Function)
|
|
48
53
|
print("Expected an exception because too much shared memory is requested")
|
|
49
54
|
assert False
|
|
@@ -55,15 +60,16 @@ def test_compile():
|
|
|
55
60
|
assert False
|
|
56
61
|
|
|
57
62
|
kernel_string = original_kernel.replace("shared_size", str(100))
|
|
63
|
+
kernel_instance = KernelInstance("vector_add", kernel_sources, kernel_string, [], None, None, dict(), [])
|
|
58
64
|
try:
|
|
59
|
-
func = dev.compile(
|
|
65
|
+
func = dev.compile( kernel_instance)
|
|
60
66
|
assert True
|
|
61
67
|
except Exception as e:
|
|
62
68
|
print("Did not expect any exception:")
|
|
63
69
|
print(str(e))
|
|
64
70
|
assert False
|
|
65
71
|
|
|
66
|
-
def dummy_func(a, b, block=0, grid=0, stream=None, texrefs=None):
|
|
72
|
+
def dummy_func(a, b, block=0, grid=0, stream=None, shared=0, texrefs=None):
|
|
67
73
|
pass
|
|
68
74
|
|
|
69
75
|
@skip_if_no_cuda
|
|
@@ -6,6 +6,7 @@ except ImportError:
|
|
|
6
6
|
from unittest.mock import patch, Mock
|
|
7
7
|
|
|
8
8
|
from kernel_tuner import cuda
|
|
9
|
+
from kernel_tuner.core import KernelSource, KernelInstance
|
|
9
10
|
|
|
10
11
|
def setup_mock(drv):
|
|
11
12
|
context = Mock()
|
|
@@ -53,7 +54,9 @@ def test_compile(drv, *args):
|
|
|
53
54
|
|
|
54
55
|
#call compile
|
|
55
56
|
kernel_string = "__global__ void vector_add()"
|
|
56
|
-
|
|
57
|
+
kernel_sources = KernelSource(kernel_string, "cuda")
|
|
58
|
+
kernel_instance = KernelInstance("vector_add", kernel_sources, kernel_string, [], None, None, dict(), [])
|
|
59
|
+
func = dev.compile(kernel_instance)
|
|
57
60
|
|
|
58
61
|
#verify behavior
|
|
59
62
|
assert dev.source_mod.call_count == 1
|
|
@@ -66,7 +69,7 @@ def test_compile(drv, *args):
|
|
|
66
69
|
assert optional_args['arch'] == 'compute_55'
|
|
67
70
|
|
|
68
71
|
|
|
69
|
-
def dummy_func(a, b, block=0, grid=0, stream=None, texrefs=None):
|
|
72
|
+
def dummy_func(a, b, block=0, grid=0, shared=0, stream=None, texrefs=None):
|
|
70
73
|
pass
|
|
71
74
|
|
|
72
75
|
@patch('kernel_tuner.cuda.nvml')
|
|
@@ -30,8 +30,7 @@ def test_interface_calls_functions(dev_interface):
|
|
|
30
30
|
kernel_name, kernel_string, size, args, tune_params = get_fake_kernel()
|
|
31
31
|
tune_kernel(kernel_name, kernel_string, size, args, tune_params, verbose=True)
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
dev.compile.assert_called_once_with("fake_kernel", expected)
|
|
33
|
+
dev.compile.assert_called_once()
|
|
35
34
|
dev.benchmark.assert_called_once_with('compile', 'ready_argument_list', (128, 1, 1), (10, 1, 1))
|
|
36
35
|
|
|
37
36
|
@patch('kernel_tuner.core.CudaFunctions')
|
|
@@ -45,7 +44,8 @@ def test_interface_handles_max_threads(dev_interface):
|
|
|
45
44
|
kernel_string = "__global__ void fake_kernel(int number)"
|
|
46
45
|
tune_kernel("fake_kernel", kernel_string, (1,1), [numpy.int32(0)], tune_params, lang="CUDA")
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
# verify that only a single instance of the kernel is compiled
|
|
48
|
+
dev.compile.assert_called_once()
|
|
49
49
|
|
|
50
50
|
@patch('kernel_tuner.core.CudaFunctions')
|
|
51
51
|
def test_interface_handles_compile_error(dev_interface):
|
|
@@ -2,12 +2,15 @@ import numpy as np
|
|
|
2
2
|
from .context import skip_if_no_opencl
|
|
3
3
|
|
|
4
4
|
from kernel_tuner import opencl
|
|
5
|
+
from kernel_tuner.core import KernelSource, KernelInstance
|
|
6
|
+
|
|
5
7
|
try:
|
|
6
8
|
import pyopencl
|
|
7
9
|
except Exception:
|
|
8
10
|
pass
|
|
9
11
|
|
|
10
12
|
|
|
13
|
+
|
|
11
14
|
@skip_if_no_opencl
|
|
12
15
|
def test_ready_argument_list():
|
|
13
16
|
|
|
@@ -41,10 +44,12 @@ def test_compile():
|
|
|
41
44
|
}
|
|
42
45
|
"""
|
|
43
46
|
|
|
47
|
+
kernel_sources = KernelSource(original_kernel, "opencl")
|
|
44
48
|
kernel_string = original_kernel.replace("shared_size", str(1024))
|
|
49
|
+
kernel_instance = KernelInstance("sum", kernel_sources, kernel_string, [], None, None, dict(), [])
|
|
45
50
|
|
|
46
51
|
dev = opencl.OpenCLFunctions(0)
|
|
47
|
-
func = dev.compile(
|
|
52
|
+
func = dev.compile(kernel_instance)
|
|
48
53
|
|
|
49
54
|
assert isinstance(func, pyopencl.Kernel)
|
|
50
55
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|