kernel-tuner 0.1.3__tar.gz → 0.1.4__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.1.3 → kernel_tuner-0.1.4}/PKG-INFO +30 -31
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/README.rst +29 -30
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/c.py +86 -52
- kernel_tuner-0.1.4/kernel_tuner/core.py +198 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/cuda.py +22 -9
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/interface.py +56 -49
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/opencl.py +20 -3
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/runners/random_sample.py +12 -17
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/runners/sequential_brute_force.py +11 -17
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/util.py +21 -31
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner.egg-info/PKG-INFO +30 -31
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/setup.py +1 -1
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_c_functions.py +10 -7
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_cuda_mocked.py +3 -3
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_interface.py +11 -6
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_kernel_tuner.py +12 -16
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_runners.py +1 -1
- kernel_tuner-0.1.3/kernel_tuner/core.py +0 -158
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/LICENSE +0 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/MANIFEST.in +0 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/__init__.py +0 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/runners/__init__.py +0 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner.egg-info/SOURCES.txt +0 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner.egg-info/dependency_links.txt +0 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner.egg-info/requires.txt +0 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner.egg-info/top_level.txt +0 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/requirements.txt +0 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/setup.cfg +2 -2
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_cuda_functions.py +0 -0
- {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_opencl_functions.py +0 -0
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 1.1
|
|
2
2
|
Name: kernel_tuner
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A simple CUDA/OpenCL kernel tuner in Python
|
|
5
5
|
Home-page: http://benvanwerkhoven.github.io/kernel_tuner/
|
|
6
6
|
Author: Ben van Werkhoven
|
|
7
7
|
Author-email: b.vanwerkhoven@esciencecenter.nl
|
|
8
8
|
License: Apache 2.0
|
|
9
|
-
Description: A simple CUDA/OpenCL kernel tuner in Python
|
|
10
|
-
|
|
9
|
+
Description: Kernel Tuner: A simple CUDA/OpenCL kernel tuner in Python
|
|
10
|
+
=========================================================
|
|
11
11
|
|
|
12
12
|
|Build Status| |Codacy Badge| |Codacy Badge2|
|
|
13
13
|
|
|
14
|
-
The
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
kernel code.
|
|
14
|
+
The Kernel Tuner is a - as simple as possible - tool for testing and auto-tuning
|
|
15
|
+
CUDA and OpenCL kernels. This means that any CUDA or OpenCL kernel can be tuned
|
|
16
|
+
without requiring extensive changes to the original kernel code.
|
|
18
17
|
|
|
19
18
|
A very common problem in GPU programming is that some combination of
|
|
20
19
|
thread block dimensions and other kernel parameters, like tiling or
|
|
@@ -22,18 +21,16 @@ Description: A simple CUDA/OpenCL kernel tuner in Python
|
|
|
22
21
|
kernel configurations. The goal of auto-tuning is to automate the
|
|
23
22
|
process of finding the best performing configuration for a given device.
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
The Kernel Tuner offers two main functions which are called run\_kernel and
|
|
25
|
+
tune\_kernel. With run\_kernel it's very easy to write unit tests for your GPU
|
|
26
|
+
kernels in Python. With tune\_kernel you can auto-tune your kernels using only a
|
|
27
|
+
simple Python script. These functions are very flexible and come with a lot of
|
|
28
|
+
optional parameters, for a complete list see the `documentation website
|
|
29
|
+
<http://benvanwerkhoven.github.io/kernel_tuner/user-api.html>`__.
|
|
29
30
|
|
|
30
|
-
The
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
function arguments, and a dictionary of tunable parameters. There are
|
|
34
|
-
also a lot of optional parameters, for a complete list see the full
|
|
35
|
-
documentation of
|
|
36
|
-
`tune\_kernel <http://benvanwerkhoven.github.io/kernel_tuner/details.html>`__.
|
|
31
|
+
The Kernel Tuner does not introduce any new dependencies in your kernels. The
|
|
32
|
+
tuned kernels can afterwards be used independently of the programming environment,
|
|
33
|
+
whether that is using C/C++/Java/Fortran or Python doesn't matter.
|
|
37
34
|
|
|
38
35
|
Documentation
|
|
39
36
|
-------------
|
|
@@ -57,7 +54,6 @@ Description: A simple CUDA/OpenCL kernel tuner in Python
|
|
|
57
54
|
|
|
58
55
|
git clone https://github.com/benvanwerkhoven/kernel_tuner.git
|
|
59
56
|
cd kernel_tuner
|
|
60
|
-
pip install -r requirements.txt
|
|
61
57
|
pip install .
|
|
62
58
|
|
|
63
59
|
To tune CUDA kernels:
|
|
@@ -70,7 +66,9 @@ Description: A simple CUDA/OpenCL kernel tuner in Python
|
|
|
70
66
|
- First, make sure you have an OpenCL compiler for your intended OpenCL platform
|
|
71
67
|
- You can install PyOpenCL using ``pip install pyopencl``
|
|
72
68
|
|
|
73
|
-
If you need more information about how to install the Kernel Tuner and all
|
|
69
|
+
If you need more information about how to install the Kernel Tuner and all
|
|
70
|
+
dependencies see the `installation guide
|
|
71
|
+
<http://benvanwerkhoven.github.io/kernel_tuner/install.html>`__
|
|
74
72
|
|
|
75
73
|
Example usage
|
|
76
74
|
-------------
|
|
@@ -114,17 +112,18 @@ Description: A simple CUDA/OpenCL kernel tuner in Python
|
|
|
114
112
|
}
|
|
115
113
|
"""
|
|
116
114
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
115
|
+
The Kernel Tuner will detect the kernel language and select the right compiler and
|
|
116
|
+
runtime. For every kernel in the parameter space, the Kernel Tuner will insert C
|
|
117
|
+
preprocessor defines for the tunable parameters, compile, and benchmark the kernel. The
|
|
118
|
+
timing results will be printed to the console, but are also returned by tune_kernel to
|
|
119
|
+
allow further analysis. Note that this is just the default behavior, what and how
|
|
120
|
+
tune_kernel does exactly is controlled through its many `optional arguments
|
|
121
|
+
<http://benvanwerkhoven.github.io/kernel_tuner/user-api.html#kernel_tuner.tune_kernel>`__.
|
|
122
|
+
|
|
123
|
+
You can find many - more extensive - example codes, in the
|
|
124
|
+
`examples directory <https://github.com/benvanwerkhoven/kernel_tuner/blob/master/examples/>`__
|
|
125
|
+
and in the `Kernel Tuner
|
|
126
|
+
documentation pages <http://benvanwerkhoven.github.io/kernel_tuner/index.html>`__.
|
|
128
127
|
|
|
129
128
|
Tuning host and kernel code
|
|
130
129
|
---------------------------
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
A simple CUDA/OpenCL kernel tuner in Python
|
|
2
|
-
|
|
1
|
+
Kernel Tuner: A simple CUDA/OpenCL kernel tuner in Python
|
|
2
|
+
=========================================================
|
|
3
3
|
|
|
4
4
|
|Build Status| |Codacy Badge| |Codacy Badge2|
|
|
5
5
|
|
|
6
|
-
The
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
kernel code.
|
|
6
|
+
The Kernel Tuner is a - as simple as possible - tool for testing and auto-tuning
|
|
7
|
+
CUDA and OpenCL kernels. This means that any CUDA or OpenCL kernel can be tuned
|
|
8
|
+
without requiring extensive changes to the original kernel code.
|
|
10
9
|
|
|
11
10
|
A very common problem in GPU programming is that some combination of
|
|
12
11
|
thread block dimensions and other kernel parameters, like tiling or
|
|
@@ -14,18 +13,16 @@ unrolling factors, results in dramatically better performance than other
|
|
|
14
13
|
kernel configurations. The goal of auto-tuning is to automate the
|
|
15
14
|
process of finding the best performing configuration for a given device.
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
The Kernel Tuner offers two main functions which are called run\_kernel and
|
|
17
|
+
tune\_kernel. With run\_kernel it's very easy to write unit tests for your GPU
|
|
18
|
+
kernels in Python. With tune\_kernel you can auto-tune your kernels using only a
|
|
19
|
+
simple Python script. These functions are very flexible and come with a lot of
|
|
20
|
+
optional parameters, for a complete list see the `documentation website
|
|
21
|
+
<http://benvanwerkhoven.github.io/kernel_tuner/user-api.html>`__.
|
|
21
22
|
|
|
22
|
-
The
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function arguments, and a dictionary of tunable parameters. There are
|
|
26
|
-
also a lot of optional parameters, for a complete list see the full
|
|
27
|
-
documentation of
|
|
28
|
-
`tune\_kernel <http://benvanwerkhoven.github.io/kernel_tuner/details.html>`__.
|
|
23
|
+
The Kernel Tuner does not introduce any new dependencies in your kernels. The
|
|
24
|
+
tuned kernels can afterwards be used independently of the programming environment,
|
|
25
|
+
whether that is using C/C++/Java/Fortran or Python doesn't matter.
|
|
29
26
|
|
|
30
27
|
Documentation
|
|
31
28
|
-------------
|
|
@@ -49,7 +46,6 @@ examples and the tutorials:
|
|
|
49
46
|
|
|
50
47
|
git clone https://github.com/benvanwerkhoven/kernel_tuner.git
|
|
51
48
|
cd kernel_tuner
|
|
52
|
-
pip install -r requirements.txt
|
|
53
49
|
pip install .
|
|
54
50
|
|
|
55
51
|
To tune CUDA kernels:
|
|
@@ -62,7 +58,9 @@ To tune OpenCL kernels:
|
|
|
62
58
|
- First, make sure you have an OpenCL compiler for your intended OpenCL platform
|
|
63
59
|
- You can install PyOpenCL using ``pip install pyopencl``
|
|
64
60
|
|
|
65
|
-
If you need more information about how to install the Kernel Tuner and all
|
|
61
|
+
If you need more information about how to install the Kernel Tuner and all
|
|
62
|
+
dependencies see the `installation guide
|
|
63
|
+
<http://benvanwerkhoven.github.io/kernel_tuner/install.html>`__
|
|
66
64
|
|
|
67
65
|
Example usage
|
|
68
66
|
-------------
|
|
@@ -106,17 +104,18 @@ The exact same Python code can be used to tune an OpenCL kernel:
|
|
|
106
104
|
}
|
|
107
105
|
"""
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
107
|
+
The Kernel Tuner will detect the kernel language and select the right compiler and
|
|
108
|
+
runtime. For every kernel in the parameter space, the Kernel Tuner will insert C
|
|
109
|
+
preprocessor defines for the tunable parameters, compile, and benchmark the kernel. The
|
|
110
|
+
timing results will be printed to the console, but are also returned by tune_kernel to
|
|
111
|
+
allow further analysis. Note that this is just the default behavior, what and how
|
|
112
|
+
tune_kernel does exactly is controlled through its many `optional arguments
|
|
113
|
+
<http://benvanwerkhoven.github.io/kernel_tuner/user-api.html#kernel_tuner.tune_kernel>`__.
|
|
114
|
+
|
|
115
|
+
You can find many - more extensive - example codes, in the
|
|
116
|
+
`examples directory <https://github.com/benvanwerkhoven/kernel_tuner/blob/master/examples/>`__
|
|
117
|
+
and in the `Kernel Tuner
|
|
118
|
+
documentation pages <http://benvanwerkhoven.github.io/kernel_tuner/index.html>`__.
|
|
120
119
|
|
|
121
120
|
Tuning host and kernel code
|
|
122
121
|
---------------------------
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
""" This module contains the functionality for running and compiling C functions """
|
|
2
2
|
|
|
3
|
-
import numpy
|
|
4
|
-
import ctypes as C
|
|
5
|
-
import _ctypes
|
|
6
3
|
import subprocess
|
|
7
|
-
import
|
|
4
|
+
import platform
|
|
8
5
|
import errno
|
|
6
|
+
import logging
|
|
7
|
+
import ctypes as C
|
|
8
|
+
import _ctypes
|
|
9
9
|
|
|
10
|
+
import numpy
|
|
10
11
|
import numpy.ctypeslib
|
|
11
|
-
from kernel_tuner.util import get_temp_filename, delete_temp_file
|
|
12
12
|
|
|
13
|
-
import
|
|
13
|
+
from kernel_tuner.util import get_temp_filename, delete_temp_file, write_file
|
|
14
|
+
|
|
14
15
|
|
|
15
16
|
class CFunctions(object):
|
|
16
17
|
"""Class that groups the code for running and compiling C functions"""
|
|
@@ -21,20 +22,46 @@ class CFunctions(object):
|
|
|
21
22
|
:param iterations: Number of iterations used while benchmarking a kernel, 7 by default.
|
|
22
23
|
:type iterations: int
|
|
23
24
|
"""
|
|
24
|
-
self.
|
|
25
|
+
self.iterations = iterations
|
|
25
26
|
self.max_threads = 1024
|
|
26
27
|
self.compiler_options = compiler_options
|
|
27
28
|
self.lib = None
|
|
29
|
+
self.using_openmp = False
|
|
30
|
+
self.arg_mapping = dict()
|
|
28
31
|
|
|
29
|
-
#
|
|
30
|
-
self.compiler = "
|
|
32
|
+
#use gcc by default
|
|
33
|
+
self.compiler = "g++"
|
|
31
34
|
try:
|
|
32
|
-
subprocess.
|
|
35
|
+
gcc_version = str(subprocess.check_output([self.compiler, "--version"]))
|
|
36
|
+
gcc_version = gcc_version.splitlines()[0].split(" ")[-1]
|
|
37
|
+
except OSError as e:
|
|
38
|
+
raise e
|
|
39
|
+
|
|
40
|
+
#check if nvcc is available
|
|
41
|
+
self.nvcc_available = False
|
|
42
|
+
try:
|
|
43
|
+
nvcc_version = str(subprocess.check_output(["nvcc", "--version"]))
|
|
44
|
+
nvcc_version = nvcc_version.splitlines()[-1].split(" ")[-1]
|
|
45
|
+
self.nvcc_available = True
|
|
33
46
|
except OSError as e:
|
|
34
|
-
self.compiler = "gcc"
|
|
35
47
|
if e.errno != errno.ENOENT:
|
|
36
48
|
raise e
|
|
37
49
|
|
|
50
|
+
#environment info
|
|
51
|
+
env = dict()
|
|
52
|
+
env["GCC Version"] = gcc_version
|
|
53
|
+
if self.nvcc_available:
|
|
54
|
+
env["NVCC Version"] = nvcc_version
|
|
55
|
+
env["iterations"] = self.iterations
|
|
56
|
+
env["compiler_options"] = compiler_options
|
|
57
|
+
self.env = env
|
|
58
|
+
self.name = platform.processor()
|
|
59
|
+
|
|
60
|
+
def get_environment(self):
|
|
61
|
+
"""Return dictionary with information about the environment"""
|
|
62
|
+
return self.env
|
|
63
|
+
|
|
64
|
+
|
|
38
65
|
def ready_argument_list(self, arguments):
|
|
39
66
|
"""ready argument list to be passed to the C function
|
|
40
67
|
|
|
@@ -46,39 +73,30 @@ class CFunctions(object):
|
|
|
46
73
|
:returns: A list of arguments that can be passed to the C function.
|
|
47
74
|
:rtype: list()
|
|
48
75
|
"""
|
|
49
|
-
self.arg_mapping = dict()
|
|
50
76
|
ctype_args = []
|
|
77
|
+
|
|
78
|
+
dtype_map = {"int32": C.c_int32,
|
|
79
|
+
"int64": C.c_int64,
|
|
80
|
+
"float32": C.c_float,
|
|
81
|
+
"float64": C.c_double}
|
|
82
|
+
np_to_c_type_map = {numpy.int32: C.c_int32,
|
|
83
|
+
numpy.int64: C.c_int64,
|
|
84
|
+
numpy.float32: C.c_float,
|
|
85
|
+
numpy.float64: C.c_double}
|
|
86
|
+
|
|
51
87
|
for arg in arguments:
|
|
52
88
|
if isinstance(arg, numpy.ndarray):
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
ctype_args.append(arg.ctypes.data_as(C.POINTER(C.c_double)))
|
|
57
|
-
elif arg.dtype == 'int32':
|
|
58
|
-
ctype_args.append(arg.ctypes.data_as(C.POINTER(C.c_int)))
|
|
89
|
+
dtype_str = str(arg.dtype)
|
|
90
|
+
if dtype_str in dtype_map.keys():
|
|
91
|
+
ctype_args.append(arg.ctypes.data_as(C.POINTER(dtype_map[dtype_str])))
|
|
59
92
|
else:
|
|
60
93
|
raise TypeError("unknown dtype for ndarray")
|
|
61
94
|
self.arg_mapping[str(ctype_args[-1])] = arg.shape
|
|
62
|
-
elif
|
|
63
|
-
|
|
64
|
-
np_to_c_type_map = { numpy.int32: C.c_int32,
|
|
65
|
-
numpy.int64: C.c_int64,
|
|
66
|
-
numpy.float32: C.c_float,
|
|
67
|
-
numpy.float64: C.c_double }
|
|
68
|
-
if type(arg) in np_to_c_type_map:
|
|
69
|
-
ctype_args.append(np_to_c_type_map[type(arg)](arg))
|
|
70
|
-
else:
|
|
71
|
-
raise TypeError("Argument is scalar with a dtype, but does not start with int or float")
|
|
72
|
-
else:
|
|
73
|
-
if isinstance(arg, int):
|
|
74
|
-
ctype_args.append(C.c_int(arg))
|
|
75
|
-
elif isinstance(arg, float):
|
|
76
|
-
ctype_args.append(C.c_float(arg))
|
|
77
|
-
else:
|
|
78
|
-
raise TypeError("Argument is scalar without dtype, and is not instance of int or float")
|
|
95
|
+
elif isinstance(arg, tuple(np_to_c_type_map.keys())):
|
|
96
|
+
ctype_args.append(np_to_c_type_map[type(arg)](arg))
|
|
79
97
|
self.arg_mapping[str(ctype_args[-1])] = ()
|
|
80
98
|
else:
|
|
81
|
-
raise TypeError("Argument is not
|
|
99
|
+
raise TypeError("Argument is not numpy ndarray or numpy scalar %s" % type(arg))
|
|
82
100
|
|
|
83
101
|
return ctype_args
|
|
84
102
|
|
|
@@ -103,12 +121,20 @@ class CFunctions(object):
|
|
|
103
121
|
|
|
104
122
|
filename = get_temp_filename()
|
|
105
123
|
source_file = filename+".cc"
|
|
106
|
-
|
|
124
|
+
|
|
125
|
+
if not "extern \"C\"" in kernel_string:
|
|
126
|
+
kernel_string = "extern \"C\" {\n" + kernel_string + "\n}"
|
|
107
127
|
|
|
108
128
|
compiler_options = ["-fPIC"]
|
|
109
129
|
if "#include <omp.h>" in kernel_string:
|
|
130
|
+
logging.debug('set using_openmp to true')
|
|
131
|
+
self.using_openmp = True
|
|
110
132
|
compiler_options.append("-fopenmp")
|
|
111
133
|
|
|
134
|
+
if ("#include <cuda" in kernel_string) or ("__global__" in kernel_string):
|
|
135
|
+
if self.nvcc_available:
|
|
136
|
+
self.compiler = "nvcc"
|
|
137
|
+
|
|
112
138
|
if self.compiler == "nvcc":
|
|
113
139
|
source_file = source_file[:-1] + "u"
|
|
114
140
|
compiler_options = ["-Xcompiler=" + c for c in compiler_options]
|
|
@@ -120,12 +146,15 @@ class CFunctions(object):
|
|
|
120
146
|
if "CL/cl.h" in kernel_string:
|
|
121
147
|
lib_args = ["-lOpenCL"]
|
|
122
148
|
|
|
149
|
+
logging.debug('using compiler ' + self.compiler)
|
|
150
|
+
logging.debug('compiler_options ' + " ".join(compiler_options))
|
|
151
|
+
logging.debug('lib_args ' + " ".join(lib_args))
|
|
152
|
+
|
|
123
153
|
try:
|
|
124
|
-
|
|
125
|
-
f.write(kernel_string)
|
|
154
|
+
write_file(source_file, kernel_string)
|
|
126
155
|
|
|
127
156
|
subprocess.check_call([self.compiler, "-c", source_file] + compiler_options + ["-o", filename+".o"])
|
|
128
|
-
subprocess.check_call([self.compiler, filename+".o"] + compiler_options + [
|
|
157
|
+
subprocess.check_call([self.compiler, filename+".o"] + compiler_options + ["-shared", "-o", filename+".so"] + lib_args)
|
|
129
158
|
|
|
130
159
|
self.lib = numpy.ctypeslib.load_library(filename, '.')
|
|
131
160
|
|
|
@@ -175,8 +204,19 @@ class CFunctions(object):
|
|
|
175
204
|
:rtype: float
|
|
176
205
|
"""
|
|
177
206
|
results = []
|
|
178
|
-
for _ in range(self.
|
|
207
|
+
for _ in range(self.iterations):
|
|
179
208
|
value = self.run_kernel(func, c_args, threads, grid)
|
|
209
|
+
|
|
210
|
+
#I would like to replace the following with actually capturing
|
|
211
|
+
#stderr and detecting the error directly in Python, it proved
|
|
212
|
+
#however that capturing stderr for non-Python functions from Python
|
|
213
|
+
#is a rather difficult thing to do
|
|
214
|
+
#
|
|
215
|
+
#The current, less than ideal, scheme uses the convention that a
|
|
216
|
+
#negative time indicates a 'too many resources requested for launch'
|
|
217
|
+
if value < 0.0:
|
|
218
|
+
raise Exception("too many resources requested for launch")
|
|
219
|
+
|
|
180
220
|
results.append(value)
|
|
181
221
|
results = sorted(results)
|
|
182
222
|
return numpy.mean(results[1:-1])
|
|
@@ -209,16 +249,6 @@ class CFunctions(object):
|
|
|
209
249
|
|
|
210
250
|
time = func(*c_args)
|
|
211
251
|
|
|
212
|
-
#I would like to replace the following with actually capturing
|
|
213
|
-
#stderr and detecting the error directly in Python, it proved
|
|
214
|
-
#however that capturing stderr for non-Python functions from Python
|
|
215
|
-
#is a rather difficult thing to do
|
|
216
|
-
#
|
|
217
|
-
#The current, less than ideal, scheme uses the convention that a
|
|
218
|
-
#negative time indicates a 'too many resources requested for launch'
|
|
219
|
-
if time < 0.0:
|
|
220
|
-
raise Exception("too many resources requested for launch")
|
|
221
|
-
|
|
222
252
|
return time
|
|
223
253
|
|
|
224
254
|
|
|
@@ -251,4 +281,8 @@ class CFunctions(object):
|
|
|
251
281
|
|
|
252
282
|
def cleanup_lib(self):
|
|
253
283
|
""" unload the previously loaded shared library """
|
|
254
|
-
|
|
284
|
+
if not self.using_openmp:
|
|
285
|
+
#this if statement is necessary because shared libraries that use
|
|
286
|
+
#OpenMP will core dump when unloaded, this is a well-known issue with OpenMP
|
|
287
|
+
logging.debug('unloading shared library')
|
|
288
|
+
_ctypes.dlclose(self.lib._handle)
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
""" Module for grouping the core functionality needed by most runners """
|
|
2
|
+
from __future__ import print_function
|
|
3
|
+
|
|
4
|
+
import resource
|
|
5
|
+
import logging
|
|
6
|
+
import numpy
|
|
7
|
+
|
|
8
|
+
from kernel_tuner.cuda import CudaFunctions
|
|
9
|
+
from kernel_tuner.opencl import OpenCLFunctions
|
|
10
|
+
from kernel_tuner.c import CFunctions
|
|
11
|
+
import kernel_tuner.util as util
|
|
12
|
+
|
|
13
|
+
def get_device_interface(lang, device, platform, compiler_options=None):
|
|
14
|
+
""" Instantiate the device functions interface, based on language """
|
|
15
|
+
if lang == "CUDA":
|
|
16
|
+
dev = CudaFunctions(device, compiler_options=compiler_options)
|
|
17
|
+
elif lang == "OpenCL":
|
|
18
|
+
dev = OpenCLFunctions(device, platform, compiler_options=compiler_options)
|
|
19
|
+
elif lang == "C":
|
|
20
|
+
dev = CFunctions(compiler_options=compiler_options)
|
|
21
|
+
else:
|
|
22
|
+
raise Exception("Sorry, support for languages other than CUDA, OpenCL, or C is not implemented yet")
|
|
23
|
+
return dev
|
|
24
|
+
|
|
25
|
+
def run_kernel(dev, func, gpu_args, instance):
|
|
26
|
+
""" Run a compiled kernel instance on a device """
|
|
27
|
+
threads = instance["threads"]
|
|
28
|
+
grid = instance["grid"]
|
|
29
|
+
try:
|
|
30
|
+
dev.run_kernel(func, gpu_args, threads, grid)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
if "too many resources requested for launch" in str(e) or "OUT_OF_RESOURCES" in str(e):
|
|
33
|
+
logging.debug('ignoring runtime failure due to too many resources required')
|
|
34
|
+
return False
|
|
35
|
+
else:
|
|
36
|
+
logging.debug('encountered unexpected runtime failure: ' + str(e))
|
|
37
|
+
raise e
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
def check_kernel_correctness(dev, func, gpu_args, instance, answer, verbose, atol=1e-6):
|
|
41
|
+
"""runs the kernel once and checks the result against answer"""
|
|
42
|
+
logging.debug('check_kernel_correctness')
|
|
43
|
+
params = instance["params"]
|
|
44
|
+
|
|
45
|
+
#zero GPU memory for output arguments
|
|
46
|
+
for result, expected in zip(gpu_args, answer):
|
|
47
|
+
if expected is not None:
|
|
48
|
+
dev.memset(result, 0, expected.nbytes)
|
|
49
|
+
|
|
50
|
+
#run the kernel
|
|
51
|
+
if not run_kernel(dev, func, gpu_args, instance):
|
|
52
|
+
return True #runtime failure occured that should be ignored, skip correctness check
|
|
53
|
+
|
|
54
|
+
#check correctness of each output argument
|
|
55
|
+
correct = True
|
|
56
|
+
for result, expected in zip(gpu_args, answer):
|
|
57
|
+
if expected is not None:
|
|
58
|
+
result_host = numpy.zeros_like(expected)
|
|
59
|
+
dev.memcpy_dtoh(result_host, result)
|
|
60
|
+
output_test = numpy.allclose(result_host.ravel(), expected.ravel(), atol=atol)
|
|
61
|
+
if not output_test and verbose:
|
|
62
|
+
print("Error: " + util.get_config_string(params) + " detected during correctness check")
|
|
63
|
+
print("Printing kernel output and expected result, set verbose=False to suppress this debug print")
|
|
64
|
+
numpy.set_printoptions(edgeitems=50)
|
|
65
|
+
print("Kernel output:")
|
|
66
|
+
print(result_host)
|
|
67
|
+
print("Expected:")
|
|
68
|
+
print(expected)
|
|
69
|
+
correct = correct and output_test
|
|
70
|
+
del result_host
|
|
71
|
+
if not correct:
|
|
72
|
+
logging.debug('correctness check has found a correctness issue')
|
|
73
|
+
raise Exception("Error: " + util.get_config_string(params) + " failed correctness check")
|
|
74
|
+
return correct
|
|
75
|
+
|
|
76
|
+
def compile_kernel(dev, instance, verbose):
|
|
77
|
+
"""compile the kernel for this specific instance"""
|
|
78
|
+
logging.debug('compile_kernel ' + instance["name"])
|
|
79
|
+
|
|
80
|
+
#compile kernel_string into device func
|
|
81
|
+
func = None
|
|
82
|
+
try:
|
|
83
|
+
func = dev.compile(instance["name"], instance["kernel_string"])
|
|
84
|
+
except Exception as e:
|
|
85
|
+
#compiles may fail because certain kernel configurations use too
|
|
86
|
+
#much shared memory for example, the desired behavior is to simply
|
|
87
|
+
#skip over this configuration and try the next one
|
|
88
|
+
if "uses too much shared data" in str(e):
|
|
89
|
+
logging.debug('compile_kernel failed due to kernel using too much shared memory')
|
|
90
|
+
if verbose:
|
|
91
|
+
print("skipping config", instance["name"], "reason: too much shared memory used")
|
|
92
|
+
else:
|
|
93
|
+
logging.debug('compile_kernel failed due to error: ' + str(e))
|
|
94
|
+
print("Error while compiling:", instance["name"])
|
|
95
|
+
raise e
|
|
96
|
+
return func
|
|
97
|
+
|
|
98
|
+
def benchmark(dev, func, gpu_args, instance, verbose):
|
|
99
|
+
"""benchmark the kernel instance"""
|
|
100
|
+
logging.debug('benchmark ' + instance["name"])
|
|
101
|
+
threads = instance["threads"]
|
|
102
|
+
grid = instance["grid"]
|
|
103
|
+
logging.debug('thread block dimensions x,y,z=%d,%d,%d', *threads)
|
|
104
|
+
logging.debug('grid dimensions x,y,z=%d,%d,%d', *grid)
|
|
105
|
+
|
|
106
|
+
time = None
|
|
107
|
+
try:
|
|
108
|
+
time = dev.benchmark(func, gpu_args, threads, grid)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
#some launches may fail because too many registers are required
|
|
111
|
+
#to run the kernel given the current thread block size
|
|
112
|
+
#the desired behavior is to simply skip over this configuration
|
|
113
|
+
#and proceed to try the next one
|
|
114
|
+
if "too many resources requested for launch" in str(e) or "OUT_OF_RESOURCES" in str(e):
|
|
115
|
+
logging.debug('benchmark fails due to runtime failure too many resources required')
|
|
116
|
+
if verbose:
|
|
117
|
+
print("skipping config", instance["name"], "reason: too many resources requested for launch")
|
|
118
|
+
else:
|
|
119
|
+
logging.debug('benchmark encountered runtime failure: ' + str(e))
|
|
120
|
+
print("Error while benchmarking:", instance["name"])
|
|
121
|
+
raise e
|
|
122
|
+
return time
|
|
123
|
+
|
|
124
|
+
def create_kernel_instance(dev, kernel_name, original_kernel, problem_size, grid_div, params, verbose):
|
|
125
|
+
"""create kernel instance from kernel strings, parameters, problem size, grid divisors, and so on"""
|
|
126
|
+
instance_string = util.get_instance_string(params)
|
|
127
|
+
|
|
128
|
+
#setup thread block and grid dimensions
|
|
129
|
+
threads, grid = util.setup_block_and_grid(problem_size, grid_div, params)
|
|
130
|
+
if numpy.prod(threads) > dev.max_threads:
|
|
131
|
+
if verbose:
|
|
132
|
+
print("skipping config", instance_string, "reason: too many threads per block")
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
temp_files = dict()
|
|
136
|
+
#obtain the kernel_string and prepare additional files, if any
|
|
137
|
+
if isinstance(original_kernel, list):
|
|
138
|
+
kernel_string, temp_files = util.prepare_list_of_files(original_kernel, params, grid)
|
|
139
|
+
else:
|
|
140
|
+
kernel_string = util.get_kernel_string(original_kernel)
|
|
141
|
+
|
|
142
|
+
#prepare kernel_string for compilation
|
|
143
|
+
name, kernel_string = util.setup_kernel_strings(kernel_name, kernel_string, params, grid)
|
|
144
|
+
|
|
145
|
+
#collect everything we know about this instance in a dict and return it
|
|
146
|
+
instance = dict()
|
|
147
|
+
instance["name"] = name
|
|
148
|
+
instance["kernel_string"] = kernel_string
|
|
149
|
+
instance["temp_files"] = temp_files
|
|
150
|
+
instance["threads"] = threads
|
|
151
|
+
instance["grid"] = grid
|
|
152
|
+
instance["params"] = params
|
|
153
|
+
return instance
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def compile_and_benchmark(dev, gpu_args, kernel_name, original_kernel, params,
|
|
157
|
+
problem_size, grid_div, cmem_args, answer, atol, verbose):
|
|
158
|
+
""" Compile and benchmark a kernel instance based on kernel strings and parameters """
|
|
159
|
+
|
|
160
|
+
instance_string = util.get_instance_string(params)
|
|
161
|
+
|
|
162
|
+
logging.debug('compile_and_benchmark ' + instance_string)
|
|
163
|
+
mem_usage = round(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss/1024.0, 1)
|
|
164
|
+
logging.debug('Memory usage : %2.2f MB', mem_usage)
|
|
165
|
+
|
|
166
|
+
instance = create_kernel_instance(dev, kernel_name, original_kernel, problem_size, grid_div, params, verbose)
|
|
167
|
+
if instance is None:
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
#compile the kernel
|
|
172
|
+
func = compile_kernel(dev, instance, verbose)
|
|
173
|
+
if func is None:
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
#add constant memory arguments to compiled module
|
|
177
|
+
if cmem_args is not None:
|
|
178
|
+
dev.copy_constant_memory_args(cmem_args)
|
|
179
|
+
|
|
180
|
+
#test kernel for correctness and benchmark
|
|
181
|
+
if answer is not None:
|
|
182
|
+
check_kernel_correctness(dev, func, gpu_args, instance, answer, verbose, atol)
|
|
183
|
+
|
|
184
|
+
#benchmark
|
|
185
|
+
time = benchmark(dev, func, gpu_args, instance, verbose)
|
|
186
|
+
|
|
187
|
+
except Exception as e:
|
|
188
|
+
#dump kernel_string to temp file
|
|
189
|
+
temp_filename = util.get_temp_filename() + ".c"
|
|
190
|
+
util.write_file(temp_filename, instance["kernel_string"])
|
|
191
|
+
print("Error while compiling or benchmarking, see source files: " + temp_filename + " ".join(instance["temp_files"].values()))
|
|
192
|
+
raise e
|
|
193
|
+
|
|
194
|
+
#clean up any temporary files, if no error occured
|
|
195
|
+
for v in instance["temp_files"].values():
|
|
196
|
+
util.delete_temp_file(v)
|
|
197
|
+
|
|
198
|
+
return time
|