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.
Files changed (30) hide show
  1. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/PKG-INFO +30 -31
  2. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/README.rst +29 -30
  3. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/c.py +86 -52
  4. kernel_tuner-0.1.4/kernel_tuner/core.py +198 -0
  5. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/cuda.py +22 -9
  6. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/interface.py +56 -49
  7. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/opencl.py +20 -3
  8. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/runners/random_sample.py +12 -17
  9. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/runners/sequential_brute_force.py +11 -17
  10. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/util.py +21 -31
  11. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner.egg-info/PKG-INFO +30 -31
  12. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/setup.py +1 -1
  13. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_c_functions.py +10 -7
  14. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_cuda_mocked.py +3 -3
  15. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_interface.py +11 -6
  16. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_kernel_tuner.py +12 -16
  17. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_runners.py +1 -1
  18. kernel_tuner-0.1.3/kernel_tuner/core.py +0 -158
  19. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/LICENSE +0 -0
  20. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/MANIFEST.in +0 -0
  21. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/__init__.py +0 -0
  22. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner/runners/__init__.py +0 -0
  23. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner.egg-info/SOURCES.txt +0 -0
  24. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner.egg-info/dependency_links.txt +0 -0
  25. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner.egg-info/requires.txt +0 -0
  26. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/kernel_tuner.egg-info/top_level.txt +0 -0
  27. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/requirements.txt +0 -0
  28. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/setup.cfg +2 -2
  29. {kernel_tuner-0.1.3 → kernel_tuner-0.1.4}/test/test_cuda_functions.py +0 -0
  30. {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
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 goal of this project is to provide a - as simple as possible - tool
15
- for tuning CUDA and OpenCL kernels. This implies that any CUDA or OpenCL
16
- kernel can be tuned without requiring extensive changes to the original
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
- This kernel tuner aims that you can directly use the tuned kernel
26
- without introducing any new dependencies. The tuned kernels can
27
- afterwards be used independently of the programming environment, whether
28
- that is using C/C++/Java/Fortran or Python doesn't matter.
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 kernel\_tuner module currently only contains main one function which
31
- is called tune\_kernel to which you pass at least the kernel name, a
32
- string containing the kernel code, the problem size, a list of kernel
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 dependencies see the `installation guide <http://benvanwerkhoven.github.io/kernel_tuner/install.html>`__
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
- Or even just a C function, see the example
118
- `here <https://github.com/benvanwerkhoven/kernel_tuner/blob/master/examples/c/vector_add.py>`__.
119
-
120
- You can find these and many - more extensive - example codes, in the
121
- `examples <https://github.com/benvanwerkhoven/kernel_tuner/blob/master/examples/>`__
122
- directory.
123
-
124
- See the `full
125
- documentation <http://benvanwerkhoven.github.io/kernel_tuner/index.html>`__
126
- for several highly detailed tutorial-style explanations of example
127
- kernels and the scripts to tune them.
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 goal of this project is to provide a - as simple as possible - tool
7
- for tuning CUDA and OpenCL kernels. This implies that any CUDA or OpenCL
8
- kernel can be tuned without requiring extensive changes to the original
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
- This kernel tuner aims that you can directly use the tuned kernel
18
- without introducing any new dependencies. The tuned kernels can
19
- afterwards be used independently of the programming environment, whether
20
- that is using C/C++/Java/Fortran or Python doesn't matter.
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 kernel\_tuner module currently only contains main one function which
23
- is called tune\_kernel to which you pass at least the kernel name, a
24
- string containing the kernel code, the problem size, a list of kernel
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 dependencies see the `installation guide <http://benvanwerkhoven.github.io/kernel_tuner/install.html>`__
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
- Or even just a C function, see the example
110
- `here <https://github.com/benvanwerkhoven/kernel_tuner/blob/master/examples/c/vector_add.py>`__.
111
-
112
- You can find these and many - more extensive - example codes, in the
113
- `examples <https://github.com/benvanwerkhoven/kernel_tuner/blob/master/examples/>`__
114
- directory.
115
-
116
- See the `full
117
- documentation <http://benvanwerkhoven.github.io/kernel_tuner/index.html>`__
118
- for several highly detailed tutorial-style explanations of example
119
- kernels and the scripts to tune them.
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 os
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 logging
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.ITERATIONS = iterations
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
- #test if nvcc is available, otherwise use gcc
30
- self.compiler = "nvcc"
32
+ #use gcc by default
33
+ self.compiler = "g++"
31
34
  try:
32
- subprocess.check_call([self.compiler, "--version"], stdout=open(os.devnull, 'w'))
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
- if arg.dtype == 'float32':
54
- ctype_args.append(arg.ctypes.data_as(C.POINTER(C.c_float)))
55
- elif arg.dtype == 'float64':
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 numpy.isscalar(arg):
63
- if hasattr(arg, 'dtype'):
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 a numpy.ndarray and is not a scalar %s" % type(arg))
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
- kernel_string = "extern \"C\" {\n" + kernel_string + "\n}"
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
- with open(source_file, 'w') as f:
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 + [ "-shared", "-o", filename+".so"] + lib_args)
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.ITERATIONS):
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
- _ctypes.dlclose(self.lib._handle)
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