kernel-tuner 1.3.3__tar.gz → 1.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/PKG-INFO +4 -3
  2. kernel_tuner-1.4.0/kernel_tuner/__init__.py +22 -0
  3. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/cupy.py +13 -7
  4. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/hip/hip.py +47 -4
  5. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/nvcuda.py +133 -25
  6. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/pycuda.py +7 -6
  7. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/core.py +116 -71
  8. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/file_utils.py +40 -17
  9. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/integration.py +1 -29
  10. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/interface.py +204 -67
  11. kernel_tuner-1.4.0/kernel_tuner/observers/amd.py +401 -0
  12. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/nvml.py +91 -24
  13. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/pmt.py +10 -5
  14. kernel_tuner-1.4.0/kernel_tuner/runners/parallel.py +405 -0
  15. kernel_tuner-1.4.0/kernel_tuner/runners/runner.py +38 -0
  16. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/runners/sequential.py +69 -39
  17. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/runners/simulation.py +64 -52
  18. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/searchspace.py +21 -20
  19. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/bayes_opt.py +27 -0
  20. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/common.py +114 -68
  21. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/diff_evo.py +4 -4
  22. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/firefly_algorithm.py +7 -6
  23. kernel_tuner-1.4.0/kernel_tuner/strategies/gen_adaptive_tabu_greywolf.py +239 -0
  24. kernel_tuner-1.4.0/kernel_tuner/strategies/gen_hybrid_vndx.py +236 -0
  25. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/genetic_algorithm.py +11 -14
  26. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/greedy_ils.py +2 -3
  27. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/greedy_mls.py +2 -3
  28. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/hillclimbers.py +18 -16
  29. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/pso.py +19 -14
  30. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/pyatf_strategies.py +1 -1
  31. kernel_tuner-1.4.0/kernel_tuner/strategies/pymoo_minimize.py +256 -0
  32. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/random_sample.py +5 -8
  33. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/simulated_annealing.py +1 -1
  34. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/util.py +302 -74
  35. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/utils/nvcuda.py +25 -4
  36. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/pyproject.toml +4 -3
  37. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/context.py +9 -1
  38. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/strategies/test_bayesian_optimization.py +2 -0
  39. kernel_tuner-1.4.0/test/strategies/test_cache_time_energy.json +48 -0
  40. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/strategies/test_common.py +10 -10
  41. kernel_tuner-1.4.0/test/strategies/test_pymoo_strategies.py +36 -0
  42. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/strategies/test_strategies.py +5 -4
  43. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_cache_file.json +2 -1
  44. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_cache_file_T1_input.json +2 -2
  45. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_common.py +14 -6
  46. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_core.py +36 -69
  47. kernel_tuner-1.4.0/test/test_cuda_functions.py +154 -0
  48. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_energy.py +2 -2
  49. kernel_tuner-1.4.0/test/test_lazy_imports.py +47 -0
  50. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_observers.py +21 -3
  51. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_runners.py +18 -11
  52. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_util_functions.py +114 -9
  53. kernel_tuner-1.3.3/kernel_tuner/__init__.py +0 -6
  54. kernel_tuner-1.3.3/kernel_tuner/runners/runner.py +0 -22
  55. kernel_tuner-1.3.3/test/test_cuda_functions.py +0 -60
  56. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/LICENSE +0 -0
  57. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/README.md +0 -0
  58. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/accuracy.py +0 -0
  59. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/__init__.py +0 -0
  60. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/backend.py +0 -0
  61. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/compiler.py +0 -0
  62. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/hip/__init__.py +0 -0
  63. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/hip/util.py +0 -0
  64. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/hypertuner.py +0 -0
  65. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/backends/opencl.py +0 -0
  66. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/energy/__init__.py +0 -0
  67. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/energy/energy.py +0 -0
  68. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/hyper.py +0 -0
  69. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/kernelbuilder.py +0 -0
  70. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/__init__.py +0 -0
  71. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/compiler.py +0 -0
  72. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/cupy.py +0 -0
  73. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/hip.py +0 -0
  74. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/ncu.py +0 -0
  75. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/nvcuda.py +0 -0
  76. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/observer.py +0 -0
  77. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/opencl.py +0 -0
  78. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/powersensor.py +0 -0
  79. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/pycuda.py +0 -0
  80. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/register.py +0 -0
  81. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/observers/tegra.py +0 -0
  82. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/runners/__init__.py +0 -0
  83. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/schema/T1/1.0.0/input-schema.json +0 -0
  84. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/schema/T4/1.0.0/metadata-schema.json +0 -0
  85. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/schema/T4/1.0.0/results-schema.json +0 -0
  86. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/__init__.py +0 -0
  87. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/basinhopping.py +0 -0
  88. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/brute_force.py +0 -0
  89. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/dual_annealing.py +0 -0
  90. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/minimize.py +0 -0
  91. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/mls.py +0 -0
  92. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/ordered_greedy_mls.py +0 -0
  93. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/skopt.py +0 -0
  94. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/strategies/wrapper.py +0 -0
  95. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/utils/__init__.py +0 -0
  96. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/kernel_tuner/utils/directives.py +0 -0
  97. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/__init__.py +0 -0
  98. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/conftest.py +0 -0
  99. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/convolution.cu +0 -0
  100. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/strategies/__init__.py +0 -0
  101. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/strategies/test_cache_file.json +0 -0
  102. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/strategies/test_diff_evo.py +0 -0
  103. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/strategies/test_genetic_algorithm.py +0 -0
  104. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/synthetic_fp32_cache_NVIDIA_RTX_A4000.json +0 -0
  105. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_T1_input.json +0 -0
  106. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_accuracy.py +0 -0
  107. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_backend.py +0 -0
  108. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_compiler_functions.py +0 -0
  109. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_cupy_functions.py +0 -0
  110. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_custom_optimizer.py +0 -0
  111. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_file_utils.py +0 -0
  112. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_hip_functions.py +0 -0
  113. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_hyper.py +0 -0
  114. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_integration.py +0 -0
  115. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_kernelbuilder.py +0 -0
  116. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_nvml_mocked.py +0 -0
  117. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_opencl_functions.py +0 -0
  118. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_pycuda_functions.py +0 -0
  119. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_pycuda_mocked.py +0 -0
  120. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_searchspace.py +0 -0
  121. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_time_budgets.py +0 -0
  122. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/test_toml_file.py +0 -0
  123. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/utils/__init__.py +0 -0
  124. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/utils/nvcuda.py +0 -0
  125. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/utils/test_directives.py +0 -0
  126. {kernel_tuner-1.3.3 → kernel_tuner-1.4.0}/test/vector_add.cu +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kernel_tuner
3
- Version: 1.3.3
3
+ Version: 1.4.0
4
4
  Summary: An easy to use CUDA/OpenCL kernel tuner in Python
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -30,6 +30,8 @@ Provides-Extra: cuda-opencl
30
30
  Provides-Extra: hip
31
31
  Provides-Extra: opencl
32
32
  Provides-Extra: tutorial
33
+ Requires-Dist: cuda-python (>=12.6.0) ; extra == "cuda"
34
+ Requires-Dist: cuda-python (>=12.6.0) ; extra == "cuda-opencl"
33
35
  Requires-Dist: hip-python ; extra == "hip"
34
36
  Requires-Dist: jsonschema
35
37
  Requires-Dist: jupyter (>=1.0.0) ; extra == "tutorial"
@@ -39,8 +41,7 @@ Requires-Dist: nvidia-ml-py (>=12.535.108) ; extra == "cuda"
39
41
  Requires-Dist: nvidia-ml-py (>=12.535.108) ; extra == "tutorial"
40
42
  Requires-Dist: packaging
41
43
  Requires-Dist: pandas (>=2.0.0)
42
- Requires-Dist: pycuda (>=2024.1) ; extra == "cuda-opencl"
43
- Requires-Dist: pycuda (>=2025.1) ; extra == "cuda"
44
+ Requires-Dist: pymoo (>=0.6.1.6)
44
45
  Requires-Dist: pynvml (>=11.4.1) ; extra == "cuda"
45
46
  Requires-Dist: pyopencl ; extra == "cuda-opencl"
46
47
  Requires-Dist: pyopencl ; extra == "opencl"
@@ -0,0 +1,22 @@
1
+ from importlib.metadata import version
2
+
3
+ from kernel_tuner.interface import run_kernel, tune_kernel, tune_kernel_T1, tune_cache
4
+
5
+ __version__ = version(__package__)
6
+
7
+ __all__ = [
8
+ "create_device_targets",
9
+ "run_kernel",
10
+ "store_results",
11
+ "tune_kernel",
12
+ "tune_cache",
13
+ "tune_kernel_T1",
14
+ "__version__",
15
+ ]
16
+
17
+
18
+ def __getattr__(name):
19
+ if name in ("store_results", "create_device_targets"):
20
+ from kernel_tuner import integration
21
+ return getattr(integration, name)
22
+ raise AttributeError(f"module 'kernel_tuner' has no attribute {name!r}")
@@ -2,6 +2,7 @@
2
2
  from __future__ import print_function
3
3
 
4
4
  import numpy as np
5
+ import uuid
5
6
 
6
7
  from kernel_tuner.backends.backend import GPUBackend
7
8
  from kernel_tuner.observers.cupy import CupyRuntimeObserver
@@ -61,28 +62,33 @@ class CupyFunctions(GPUBackend):
61
62
  # default dynamically allocated shared memory size, can be overwritten using smem_args
62
63
  self.smem_size = 0
63
64
 
64
- # setup observers
65
- self.observers = observers or []
66
- self.observers.append(CupyRuntimeObserver(self))
67
- for obs in self.observers:
68
- obs.register_device(self)
69
-
70
65
  # collect environment information
71
66
  env = dict()
72
67
  cupy_info = str(get_runtime_info()).split("\n")[:-1]
73
68
  info_dict = {
74
- s.split(":")[0].strip(): s.split(":")[1].strip() for s in cupy_info
69
+ s.split(":", 1)[0].strip(): s.split(":", 1)[1].strip() for s in cupy_info
75
70
  }
76
71
  env["device_name"] = info_dict[f"Device {device} Name"]
72
+ env["pci_bus_id"] = info_dict[f"Device {device} PCI Bus ID"]
77
73
 
78
74
  env["cuda_version"] = cp.cuda.runtime.driverGetVersion()
79
75
  env["compute_capability"] = self.cc
80
76
  env["iterations"] = self.iterations
81
77
  env["compiler_options"] = compiler_options
82
78
  env["device_properties"] = self.devprops
79
+
80
+ props = cp.cuda.runtime.getDeviceProperties(device)
81
+ env["uuid"] = str(uuid.UUID(bytes=props["uuid"]))
82
+
83
83
  self.env = env
84
84
  self.name = env["device_name"]
85
85
 
86
+ # setup observers
87
+ self.observers = observers or []
88
+ self.observers.append(CupyRuntimeObserver(self))
89
+ for obs in self.observers:
90
+ obs.register_device(self)
91
+
86
92
  def ready_argument_list(self, arguments):
87
93
  """Ready argument list to be passed to the kernel, allocates gpu mem.
88
94
 
@@ -3,6 +3,8 @@
3
3
  import ctypes
4
4
  import ctypes.util
5
5
  import logging
6
+ import json
7
+ import uuid
6
8
 
7
9
  import numpy as np
8
10
 
@@ -32,8 +34,35 @@ dtype_map = {
32
34
 
33
35
  hipSuccess = 0
34
36
 
37
+
38
+ def extract_safe_properties(props):
39
+ """Extract those properties that are 'safe' (i.e, json serializable)"""
40
+ result = dict()
41
+
42
+ for key in props.PROPERTIES():
43
+ try:
44
+ value = getattr(props, key)
45
+ if isinstance(value, (str, int, float)):
46
+ # These are safe
47
+ result[key] = value
48
+ elif isinstance(value, (bytes, bytearray)):
49
+ # bytes/bytearray is typically a string
50
+ result[key] = value.decode("utf-8").rstrip("\x00")
51
+ else:
52
+ # As last option, try to convert to json
53
+ result[key] = json.loads(json.dumps(value))
54
+ except Exception:
55
+ continue
56
+
57
+ # Remove the 'reserved' fields as they are just filled with zeros
58
+ result.pop("reserved", None)
59
+ result.pop("hipReserved", None)
60
+
61
+ return result
62
+
63
+
35
64
  class HipFunctions(GPUBackend):
36
- """Class that groups the HIP functions on maintains state about the device."""
65
+ """Class that groups the HIP functions and maintains state about the device."""
37
66
 
38
67
  def __init__(self, device=0, iterations=7, compiler_options=None, observers=None):
39
68
  """Instantiate HipFunctions object used for interacting with the HIP device.
@@ -72,6 +101,13 @@ class HipFunctions(GPUBackend):
72
101
  env["device_name"] = self.name
73
102
  env["iterations"] = self.iterations
74
103
  env["compiler_options"] = compiler_options
104
+ env["pci_bus_id"] = props.pciBusID
105
+ env["pci_domain_id"] = props.pciDomainID
106
+ env["pci_device_id"] = props.pciDeviceID
107
+ env["uuid"] = str(uuid.UUID(bytes=props.uuid.bytes))
108
+ env["hip_driver_version"] = hip_check(hip.hipDriverGetVersion())
109
+ env["hip_runtime_version"] = hip_check(hip.hipRuntimeGetVersion())
110
+ env["device_properties"] = extract_safe_properties(props)
75
111
  self.env = env
76
112
 
77
113
  # Create stream and events
@@ -149,13 +185,16 @@ class HipFunctions(GPUBackend):
149
185
  # Format kernel string
150
186
  kernel_string = kernel_instance.kernel_string
151
187
  kernel_name = kernel_instance.name
152
- if 'extern "C"' not in kernel_string:
153
- kernel_string = 'extern "C" {\n' + kernel_string + "\n}"
188
+ expression_name = kernel_name.encode()
154
189
 
155
190
  # Create program
156
191
  prog = hip_check(hiprtc.hiprtcCreateProgram(kernel_string.encode(), kernel_name.encode(), 0, [], []))
157
192
 
158
193
  try:
194
+ # Add the kernel as an expression. This forces hiprtc to instantiate the kernel if it
195
+ # is templated or if it is in a namespace.
196
+ hip_check(hiprtc.hiprtcAddNameExpression(prog, expression_name))
197
+
159
198
  # Get device properties
160
199
  props = hip.hipDeviceProp_t()
161
200
  hip_check(hip.hipGetDeviceProperties(props, 0))
@@ -174,6 +213,10 @@ class HipFunctions(GPUBackend):
174
213
  hip_check(hiprtc.hiprtcGetProgramLog(prog, log))
175
214
  raise RuntimeError(log.decode())
176
215
 
216
+ # Get the lowered name. This is the name that can be used in hipModuleGetFunction to
217
+ # get the kernel. For templated kernels, this differs from the original kernel name.
218
+ lowered_name = hip_check(hiprtc.hiprtcGetLoweredName(prog, expression_name))
219
+
177
220
  # Get compiled code
178
221
  code_size = hip_check(hiprtc.hiprtcGetCodeSize(prog))
179
222
  code = bytearray(code_size)
@@ -182,7 +225,7 @@ class HipFunctions(GPUBackend):
182
225
  # Load module and get function
183
226
  module = hip_check(hip.hipModuleLoadData(code))
184
227
  self.current_module = module
185
- kernel = hip_check(hip.hipModuleGetFunction(module, kernel_name.encode()))
228
+ kernel = hip_check(hip.hipModuleGetFunction(module, lowered_name))
186
229
 
187
230
  except Exception as e:
188
231
  # Cleanup
@@ -2,11 +2,13 @@
2
2
  from warnings import warn
3
3
 
4
4
  import numpy as np
5
+ import uuid
6
+ import os
5
7
 
6
8
  from kernel_tuner.backends.backend import GPUBackend
7
9
  from kernel_tuner.observers.nvcuda import CudaRuntimeObserver
8
10
  from kernel_tuner.util import SkippableFailure
9
- from kernel_tuner.utils.nvcuda import cuda_error_check, to_valid_nvrtc_gpu_arch_cc
11
+ from kernel_tuner.utils.nvcuda import cuda_error_check, to_valid_nvrtc_gpu_arch_cc, find_cuda_home, _check
10
12
 
11
13
  # embedded in try block to be able to generate documentation
12
14
  # and run tests without cuda-python installed
@@ -74,9 +76,6 @@ class CudaFunctions(GPUBackend):
74
76
  self.current_module = None
75
77
  self.func = None
76
78
  self.compiler_options = compiler_options or []
77
- self.compiler_options_bytes = []
78
- for option in self.compiler_options:
79
- self.compiler_options_bytes.append(str(option).encode("UTF-8"))
80
79
 
81
80
  # create a stream and events
82
81
  err, self.stream = driver.cuStreamCreate(0)
@@ -85,34 +84,119 @@ class CudaFunctions(GPUBackend):
85
84
  cuda_error_check(err)
86
85
  err, self.end = driver.cuEventCreate(0)
87
86
  cuda_error_check(err)
87
+ self.current_sm_percentage = 100
88
+ self.green_ctx_cache = {}
89
+ self.green_ctx = None
88
90
 
89
91
  # default dynamically allocated shared memory size, can be overwritten using smem_args
90
92
  self.smem_size = 0
91
93
 
92
- # setup observers
93
- self.observers = observers or []
94
- self.observers.append(CudaRuntimeObserver(self))
95
- for observer in self.observers:
96
- observer.register_device(self)
97
-
98
94
  # collect environment information
99
95
  err, device_properties = runtime.cudaGetDeviceProperties(device)
100
96
  cuda_error_check(err)
101
97
  env = dict()
98
+ env["uuid"] = str(uuid.UUID(bytes=device_properties.uuid.bytes))
102
99
  env["device_name"] = device_properties.name.decode()
103
100
  env["cuda_version"] = driver.CUDA_VERSION
104
101
  env["compute_capability"] = self.cc
105
102
  env["iterations"] = self.iterations
106
103
  env["compiler_options"] = self.compiler_options
107
104
  env["device_properties"] = str(device_properties).replace("\n", ", ")
105
+
106
+ # We must use `cudaDeviceGetPCIBusId` to get the PCI bus string
107
+ # It returns a series of bytes containing a null byte, not a `str`
108
+ err, pci_bus = runtime.cudaDeviceGetPCIBusId(32, device) # 32 = length?
109
+ cuda_error_check(err)
110
+ env["pci_bus_id"] = pci_bus.decode("ascii").split("\x00", 1)[0]
111
+
108
112
  self.env = env
109
113
  self.name = env["device_name"]
110
114
 
115
+ # setup observers
116
+ self.observers = observers or []
117
+ self.observers.append(CudaRuntimeObserver(self))
118
+ for observer in self.observers:
119
+ observer.register_device(self)
120
+
111
121
  def __del__(self):
122
+ # Cleanup streams and green contexts, if any
123
+ if self.green_ctx_cache:
124
+ for val in self.green_ctx_cache.values():
125
+ green_ctx, stream, _ = val
126
+ _check(driver.cuStreamDestroy(stream))
127
+ _check(driver.cuGreenCtxDestroy(green_ctx))
128
+
129
+ # Cleanup
112
130
  for device_memory in self.allocations:
113
131
  if isinstance(device_memory, driver.CUdeviceptr):
114
- err = driver.cuMemFree(device_memory)
115
- cuda_error_check(err)
132
+ _check(driver.cuMemFree(device_memory))
133
+
134
+
135
+ def set_sm_percentage(self, sm_percentage):
136
+ """ Set the active SM percentage
137
+
138
+ Create a CUDA green context owning ~`sm_percentage` of the device's SMs
139
+ and a stream bound to it. Kernels launched afterwards are restricted
140
+ to that SM partition. Green contexts are cached in self.green_ctx_cache.
141
+ The actual number of SMs in the partition may not exactly match the
142
+ requested percentage. An observer may be used to query:
143
+
144
+ * Currently assigned number of SMs: self.assigned_sm_count
145
+ * Currently requested SM percentage: self.current_sm_percentage
146
+
147
+ Requires: CUDA >= 12.4 and a GPU that supports SM partitioning.
148
+ """
149
+
150
+ if not 0 < sm_percentage <= 100:
151
+ raise ValueError("sm_percentage must be in (0, 100]")
152
+
153
+ # Check if sm_percentage is already applied
154
+ if sm_percentage == self.current_sm_percentage:
155
+ return
156
+
157
+ # Check if this sm_percentage has been requested before
158
+ if sm_percentage in self.green_ctx_cache:
159
+ self.green_ctx, self.stream, self.assigned_sm_count = self.green_ctx_cache[sm_percentage]
160
+ self.current_sm_percentage = sm_percentage
161
+ return
162
+
163
+ # Get total SMs and desired percentage
164
+ total_sms = _check(driver.cuDeviceGetAttribute(
165
+ driver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, self.device))
166
+ want = max(1, round(total_sms * sm_percentage / 100.0))
167
+
168
+ # Full SM resource pool of the device.
169
+ sm_resource = _check(driver.cuDeviceGetDevResource(
170
+ self.device, driver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM))
171
+
172
+ # Split off one group of at least `want` SMs. The driver rounds up to the
173
+ # device's partitioning granularity, so the actual count may be larger.
174
+ groups, _nb, _remaining = _check(driver.cuDevSmResourceSplitByCount(
175
+ 1, # number of groups requested
176
+ sm_resource, # input resource
177
+ 0, # useFlags (0 = default)
178
+ want, # minCount of SMs per group
179
+ ))
180
+ group = groups[0]
181
+ assigned = group.sm.smCount
182
+
183
+ # Descriptor -> green context.
184
+ desc = _check(driver.cuDevResourceGenerateDesc([group], 1))
185
+ green_ctx = _check(driver.cuGreenCtxCreate(
186
+ desc, self.device, driver.CUgreenCtxCreate_flags.CU_GREEN_CTX_DEFAULT_STREAM))
187
+
188
+ # A stream from the green context confines launches to its SMs.
189
+ stream = _check(driver.cuGreenCtxStreamCreate(
190
+ green_ctx,
191
+ driver.CUstream_flags.CU_STREAM_NON_BLOCKING,
192
+ 0, # priority
193
+ ))
194
+ self.green_ctx_cache[sm_percentage] = (green_ctx, stream, assigned)
195
+ self.green_ctx = green_ctx
196
+ self.stream = stream
197
+ self.assigned_sm_count = assigned
198
+ self.current_sm_percentage = sm_percentage
199
+
116
200
 
117
201
  def ready_argument_list(self, arguments):
118
202
  """Ready argument list to be passed to the kernel, allocates gpu mem.
@@ -139,6 +223,7 @@ class CudaFunctions(GPUBackend):
139
223
  gpu_args.append(arg)
140
224
  return gpu_args
141
225
 
226
+
142
227
  def compile(self, kernel_instance):
143
228
  """Call the CUDA compiler to compile the kernel, return the device function.
144
229
 
@@ -154,37 +239,60 @@ class CudaFunctions(GPUBackend):
154
239
  """
155
240
  kernel_string = kernel_instance.kernel_string
156
241
  kernel_name = kernel_instance.name
242
+ expression_name = str.encode(kernel_name)
243
+ compiler_options = list(self.compiler_options)
244
+
245
+ # Add -std=c++11
246
+ if not any(opt.startswith(("-std=", "--std=")) for opt in self.compiler_options):
247
+ compiler_options.append("--std=c++11")
157
248
 
158
- # mimic pycuda behavior to wrap kernel_string in extern "C" if not in kernel_string already
159
- if 'extern "C"' not in kernel_string:
160
- kernel_string = 'extern "C" {\n' + kernel_string + "\n}"
249
+ # Add -arch
250
+ if not any(opt.startswith(("-arch", "--arch", "--gpu-architecture=")) for opt in self.compiler_options):
251
+ arch_val = to_valid_nvrtc_gpu_arch_cc(self.cc)
252
+ compiler_options.append(f"--gpu-architecture=compute_{arch_val}")
161
253
 
162
- compiler_options = self.compiler_options_bytes
163
- if not any([b"--std=" in opt for opt in compiler_options]):
164
- compiler_options.append(b"--std=c++11")
165
- if not any(["--std=" in opt for opt in self.compiler_options]):
166
- self.compiler_options.append("--std=c++11")
167
- if not any([b"--gpu-architecture=" in opt or b"-arch" in opt for opt in compiler_options]):
168
- compiler_options.append(f"--gpu-architecture=compute_{to_valid_nvrtc_gpu_arch_cc(self.cc)}".encode("UTF-8"))
169
- if not any(["--gpu-architecture=" in opt or "-arch" in opt for opt in self.compiler_options]):
170
- self.compiler_options.append(f"--gpu-architecture=compute_{to_valid_nvrtc_gpu_arch_cc(self.cc)}")
254
+ # Add CUDA home to include path
255
+ cuda_home = find_cuda_home()
256
+ if cuda_home:
257
+ cuda_include = os.path.join(cuda_home, "include")
258
+ compiler_options.append(f"-I{cuda_include}")
259
+
260
+ # nvrtcCompileProgram requires bytes instead of str
261
+ compiler_options = [str(opt).encode("UTF-8") for opt in compiler_options]
171
262
 
172
263
  err, program = nvrtc.nvrtcCreateProgram(str.encode(kernel_string), b"CUDAProgram", 0, [], [])
173
264
  try:
265
+ # Add the kernel as an expression. This is necessary for templated kernels to ensure that the
266
+ # compiler actually instantiates the kernel that we want to compile.
267
+ cuda_error_check(err)
268
+ err = nvrtc.nvrtcAddNameExpression(program, expression_name)
269
+
270
+ # Compile the program
174
271
  cuda_error_check(err)
175
272
  err = nvrtc.nvrtcCompileProgram(program, len(compiler_options), compiler_options)
273
+
274
+ # Get the PTX
176
275
  cuda_error_check(err)
177
276
  err, size = nvrtc.nvrtcGetPTXSize(program)
178
277
  cuda_error_check(err)
179
278
  buff = b" " * size
180
279
  err = nvrtc.nvrtcGetPTX(program, buff)
181
280
  cuda_error_check(err)
281
+
282
+ # Load the module
182
283
  err, self.current_module = driver.cuModuleLoadData(np.char.array(buff))
183
284
  if err == driver.CUresult.CUDA_ERROR_INVALID_PTX:
184
285
  raise SkippableFailure("uses too much shared data")
185
286
  else:
186
287
  cuda_error_check(err)
187
- err, self.func = driver.cuModuleGetFunction(self.current_module, str.encode(kernel_name))
288
+
289
+ # First, get the "lowered" name of the kernel (i.e., the name inside the PTX).
290
+ # After, we can use the lowered name to lookup the kernel in the module.
291
+ err, lowered_name = nvrtc.nvrtcGetLoweredName(program, expression_name)
292
+ cuda_error_check(err)
293
+ err, self.func = driver.cuModuleGetFunction(
294
+ self.current_module, lowered_name
295
+ )
188
296
  cuda_error_check(err)
189
297
 
190
298
  # get the number of registers per thread used in this kernel
@@ -130,15 +130,10 @@ class PyCudaFunctions(GPUBackend):
130
130
  # default dynamically allocated shared memory size, can be overwritten using smem_args
131
131
  self.smem_size = 0
132
132
 
133
- # setup observers
134
- self.observers = observers or []
135
- self.observers.append(PyCudaRuntimeObserver(self))
136
- for obs in self.observers:
137
- obs.register_device(self)
138
-
139
133
  # collect environment information
140
134
  env = dict()
141
135
  env["device_name"] = self.context.get_device().name()
136
+ env["pci_bus_id"] = self.context.get_device().pci_bus_id()
142
137
  env["cuda_version"] = ".".join([str(i) for i in drv.get_version()])
143
138
  env["compute_capability"] = self.cc
144
139
  env["iterations"] = self.iterations
@@ -147,6 +142,12 @@ class PyCudaFunctions(GPUBackend):
147
142
  self.env = env
148
143
  self.name = env["device_name"]
149
144
 
145
+ # setup observers
146
+ self.observers = observers or []
147
+ self.observers.append(PyCudaRuntimeObserver(self))
148
+ for obs in self.observers:
149
+ obs.register_device(self)
150
+
150
151
  def __del__(self):
151
152
  for gpu_mem in self.allocations:
152
153
  # if needed for when using mocks during testing