Repository navigation
Add JIT examples to CUDA docs #7918
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
3c10cff
port vecadd example
brandon-b-miller b57da5b
add kernel examples to cuda docs
brandon-b-miller f6b30f7
partially address reviews
brandon-b-miller cace1f9
continue responding to feedback
brandon-b-miller c1a383f
move figures
brandon-b-miller 3d0b7a5
addressing remaining comments
brandon-b-miller 638a52f
add a word about reduction
brandon-b-miller a079330
style
brandon-b-miller d787299
label def has _, but the reference does not
brandon-b-miller 90ffdf9
optionally regenerate figures
brandon-b-miller fb779cc
assert integration results match maple
brandon-b-miller 0df542b
Apply suggestions from code review
brandon-b-miller 9527730
Merge branch 'jit-examples' of github.com:brandon-b-miller/numba into…
brandon-b-miller b0efa05
address reviews
brandon-b-miller dffef47
remove redundant wording
brandon-b-miller 215564e
Apply suggestions from code review
brandon-b-miller 5f78980
Apply suggestions from code review
brandon-b-miller d6eedef
update test_reduction and relevant docs
brandon-b-miller dd7fc35
update montecarlo example
brandon-b-miller b779dab
Apply suggestions from code review
brandon-b-miller 943c2fd
more addressing of reviews
brandon-b-miller 023476a
Merge branch 'jit-examples' of github.com:brandon-b-miller/numba into…
brandon-b-miller 577dd37
flake8
brandon-b-miller f252db3
Apply suggestions from code review
brandon-b-miller 57ed078
updates for review
brandon-b-miller daef20d
Fixes for the Laplace example
gmarkall abadabc
small bugfix for plot=False
brandon-b-miller 8ad3ea0
small fix
brandon-b-miller 4d36f38
pacify flake8
brandon-b-miller c161e8e
Skip tests that use CG if cudadevrt is missing
gmarkall 1b77d36
Use manual configuration for test_reduction
gmarkall 09e1d6d
Skip tests that use grid sync on CC < 6.0
gmarkall File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import unittest | ||
|
|
||
| from numba.cuda.testing import CUDATestCase, skip_on_cudasim | ||
| from numba.tests.support import captured_stdout | ||
| import numpy as np | ||
|
|
||
|
|
||
| @skip_on_cudasim("cudasim doesn't support cuda import at non-top-level") | ||
| class TestCpuGpuCompat(CUDATestCase): | ||
| """ | ||
| Test compatibility of CPU and GPU functions | ||
| """ | ||
|
|
||
| def setUp(self): | ||
| # Prevent output from this test showing up when running the test suite | ||
| self._captured_stdout = captured_stdout() | ||
| self._captured_stdout.__enter__() | ||
| super().setUp() | ||
|
|
||
| def tearDown(self): | ||
| # No exception type, value, or traceback | ||
| self._captured_stdout.__exit__(None, None, None) | ||
| super().tearDown() | ||
|
|
||
| def test_ex_cpu_gpu_compat(self): | ||
| # ex_cpu_gpu_compat.import.begin | ||
| from math import pi | ||
|
|
||
| import numba | ||
| from numba import cuda | ||
| # ex_cpu_gpu_compat.import.end | ||
|
|
||
| # ex_cpu_gpu_compat.allocate.begin | ||
| X = cuda.to_device([1, 10, 234]) | ||
| Y = cuda.to_device([2, 2, 4014]) | ||
| Z = cuda.to_device([3, 14, 2211]) | ||
| results = cuda.to_device([0.0, 0.0, 0.0]) | ||
| # ex_cpu_gpu_compat.allocate.end | ||
|
|
||
| # ex_cpu_gpu_compat.define.begin | ||
| @numba.jit | ||
| def business_logic(x, y, z): | ||
| return 4 * z * (2 * x - (4 * y) / 2 * pi) | ||
| # ex_cpu_gpu_compat.define.end | ||
|
|
||
| # ex_cpu_gpu_compat.cpurun.begin | ||
| print(business_logic(1, 2, 3)) # -126.79644737231007 | ||
| # ex_cpu_gpu_compat.cpurun.end | ||
|
|
||
| # ex_cpu_gpu_compat.usegpu.begin | ||
| @cuda.jit | ||
| def f(res, xarr, yarr, zarr): | ||
| tid = cuda.grid(1) | ||
| if tid < len(xarr): | ||
| # The function decorated with numba.jit may be directly reused | ||
| res[tid] = business_logic(xarr[tid], yarr[tid], zarr[tid]) | ||
| # ex_cpu_gpu_compat.usegpu.end | ||
|
|
||
| # ex_cpu_gpu_compat.launch.begin | ||
| f.forall(len(X))(results, X, Y, Z) | ||
| print(results) | ||
| # [-126.79644737231007, 416.28324559588634, -218912930.2987788] | ||
| # ex_cpu_gpu_compat.launch.end | ||
|
|
||
| expect = [ | ||
| business_logic(x, y, z) for x, y, z in zip(X, Y, Z) | ||
| ] | ||
|
|
||
| np.testing.assert_equal( | ||
| expect, | ||
| results.copy_to_host() | ||
| ) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| import unittest | ||
|
|
||
| from numba.cuda.testing import (CUDATestCase, skip_if_cudadevrt_missing, | ||
| skip_on_cudasim) | ||
| from numba.tests.support import captured_stdout | ||
|
|
||
|
|
||
| @skip_if_cudadevrt_missing | ||
| @skip_on_cudasim("cudasim doesn't support cuda import at non-top-level") | ||
| class TestLaplace(CUDATestCase): | ||
| """ | ||
| Test simple vector addition | ||
| """ | ||
|
|
||
| def setUp(self): | ||
| # Prevent output from this test showing up when running the test suite | ||
| self._captured_stdout = captured_stdout() | ||
| self._captured_stdout.__enter__() | ||
| super().setUp() | ||
|
|
||
| def tearDown(self): | ||
| # No exception type, value, or traceback | ||
| self._captured_stdout.__exit__(None, None, None) | ||
| super().tearDown() | ||
|
|
||
| def test_ex_laplace(self): | ||
|
|
||
| # set True to regenerate the figures that | ||
| # accompany this example | ||
| plot = False | ||
|
|
||
| # ex_laplace.import.begin | ||
| import numpy as np | ||
| from numba import cuda | ||
| # ex_laplace.import.end | ||
|
|
||
| # ex_laplace.allocate.begin | ||
| # Use an odd problem size. | ||
| # This is so there can be an element truly in the "middle" for symmetry. | ||
| size = 1001 | ||
| data = np.zeros(size) | ||
|
|
||
| # Middle element is made very hot | ||
| data[500] = 10000 | ||
| buf_0 = cuda.to_device(data) | ||
|
|
||
| # This extra array is used for synchronization purposes | ||
| buf_1 = cuda.device_array_like(buf_0) | ||
|
|
||
| niter = 10000 | ||
| # ex_laplace.allocate.end | ||
|
|
||
| if plot: | ||
| import matplotlib.pyplot as plt | ||
| fig, ax = plt.subplots(figsize=(16 * 0.66, 9 * 0.66)) | ||
| plt.plot( | ||
| np.arange(len(buf_0)), | ||
| buf_0.copy_to_host(), | ||
| lw=3, | ||
| marker="*", | ||
| color='black' | ||
| ) | ||
|
|
||
| plt.title('Initial State', fontsize=24) | ||
| plt.xlabel('Position', fontsize=24) | ||
| plt.ylabel('Temperature', fontsize=24) | ||
|
|
||
| ax.set_xticks(ax.get_xticks(), fontsize=16) | ||
| ax.set_yticks(ax.get_yticks(), fontsize=16) | ||
| plt.xlim(0, len(data)) | ||
| plt.ylim(0, 10001) | ||
| plt.savefig('laplace_initial.svg') | ||
|
|
||
| # ex_laplace.kernel.begin | ||
| @cuda.jit | ||
| def solve_heat_equation(buf_0, buf_1, timesteps, k): | ||
| i = cuda.grid(1) | ||
|
|
||
| # Don't continue if our index is outside the domain | ||
| if i >= len(buf_0): | ||
| return | ||
|
|
||
| # Prepare to do a grid-wide synchronization later | ||
| grid = cuda.cg.this_grid() | ||
|
|
||
| for step in range(timesteps): | ||
| # Select the buffer from the previous timestep | ||
| if (step % 2) == 0: | ||
| data = buf_0 | ||
| next_data = buf_1 | ||
| else: | ||
| data = buf_1 | ||
| next_data = buf_0 | ||
|
|
||
| # Get the current temperature associated with this point | ||
| curr_temp = data[i] | ||
|
|
||
| # Apply formula from finite difference equation | ||
| if i == 0: | ||
| # Left wall is held at T = 0 | ||
| next_temp = curr_temp + k * (data[i + 1] - (2 * curr_temp)) | ||
| elif i == len(data) - 1: | ||
| # Right wall is held at T = 0 | ||
| next_temp = curr_temp + k * (data[i - 1] - (2 * curr_temp)) | ||
| else: | ||
| # Interior points are a weighted average of their neighbors | ||
| next_temp = curr_temp + k * ( | ||
| data[i - 1] - (2 * curr_temp) + data[i + 1] | ||
| ) | ||
|
|
||
| # Write new value to the next buffer | ||
| next_data[i] = next_temp | ||
|
|
||
| # Wait for every thread to write before moving on | ||
| grid.sync() | ||
| # ex_laplace.kernel.end | ||
|
|
||
| # ex_laplace.launch.begin | ||
| solve_heat_equation.forall(len(data))( | ||
| buf_0, buf_1, niter, 0.25 | ||
| ) | ||
| # ex_laplace.launch.end | ||
|
|
||
| results = buf_1.copy_to_host() | ||
| if plot: | ||
| fig, ax = plt.subplots(figsize=(16 * 0.66, 9 * 0.66)) | ||
| plt.plot( | ||
| np.arange(len(results)), | ||
| results, lw=3, | ||
| marker="*", | ||
| color='black' | ||
| ) | ||
| plt.title(f"T = {niter}", fontsize=24) | ||
| plt.xlabel('Position', fontsize=24) | ||
| plt.ylabel('Temperature', fontsize=24) | ||
|
|
||
| ax.set_xticks(ax.get_xticks(), fontsize=16) | ||
| ax.set_yticks(ax.get_yticks(), fontsize=16) | ||
|
|
||
| plt.ylim(0, max(results)) | ||
| plt.xlim(0, len(results)) | ||
| plt.savefig('laplace_final.svg') | ||
|
|
||
| # Integral over the domain should be equal to its initial value. | ||
| # Note that this should match the initial value of data[500] above, but | ||
| # we don't assign it to a variable because that would make the example | ||
| # code look a bit oddly verbose. | ||
| np.testing.assert_allclose(results.sum(), 10000) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import unittest | ||
|
|
||
| from numba.cuda.testing import CUDATestCase, skip_on_cudasim | ||
| from numba.tests.support import captured_stdout | ||
|
|
||
|
|
||
| @skip_on_cudasim("cudasim doesn't support cuda import at non-top-level") | ||
| class TestMonteCarlo(CUDATestCase): | ||
| """ | ||
| Test monte-carlo integration | ||
| """ | ||
|
|
||
| def setUp(self): | ||
| # Prevent output from this test showing up when running the test suite | ||
| self._captured_stdout = captured_stdout() | ||
| self._captured_stdout.__enter__() | ||
| super().setUp() | ||
|
|
||
| def tearDown(self): | ||
| # No exception type, value, or traceback | ||
| self._captured_stdout.__exit__(None, None, None) | ||
| super().tearDown() | ||
|
|
||
| def test_ex_montecarlo(self): | ||
| # ex_montecarlo.import.begin | ||
| import numba | ||
| import numpy as np | ||
| from numba import cuda | ||
| from numba.cuda.random import ( | ||
| create_xoroshiro128p_states, | ||
| xoroshiro128p_uniform_float32, | ||
| ) | ||
| # ex_montecarlo.import.end | ||
|
|
||
| # ex_montecarlo.define.begin | ||
| # number of samples, higher will lead to a more accurate answer | ||
| nsamps = 1000000 | ||
| # ex_montecarlo.define.end | ||
|
|
||
| # ex_montecarlo.kernel.begin | ||
| @cuda.jit | ||
| def mc_integrator_kernel(out, rng_states, lower_lim, upper_lim): | ||
| """ | ||
| kernel to draw random samples and evaluate the function to | ||
| be integrated at those sample values | ||
| """ | ||
| size = len(out) | ||
|
|
||
| gid = cuda.grid(1) | ||
| if gid < size: | ||
| # draw a sample between 0 and 1 on this thread | ||
| samp = xoroshiro128p_uniform_float32(rng_states, gid) | ||
|
|
||
| # normalize this sample to the limit range | ||
| samp = samp * (upper_lim - lower_lim) + lower_lim | ||
|
|
||
| # evaluate the function to be | ||
| # integrated at the normalized | ||
| # value of the sample | ||
| y = func(samp) | ||
| out[gid] = y | ||
| # ex_montecarlo.kernel.end | ||
|
|
||
| # ex_montecarlo.callfunc.begin | ||
| @cuda.reduce | ||
| def sum_reduce(a, b): | ||
| return a + b | ||
|
|
||
| def mc_integrate(lower_lim, upper_lim, nsamps): | ||
| """ | ||
| approximate the definite integral of `func` from | ||
| `lower_lim` to `upper_lim` | ||
| """ | ||
| out = cuda.to_device(np.zeros(nsamps, dtype="float32")) | ||
| rng_states = create_xoroshiro128p_states(nsamps, seed=42) | ||
|
|
||
| # jit the function for use in CUDA kernels | ||
|
|
||
| mc_integrator_kernel.forall(nsamps)( | ||
| out, rng_states, lower_lim, upper_lim | ||
| ) | ||
| # normalization factor to convert | ||
| # to the average: (b - a)/(N - 1) | ||
| factor = (upper_lim - lower_lim) / (nsamps - 1) | ||
|
|
||
| return sum_reduce(out) * factor | ||
| # ex_montecarlo.callfunc.end | ||
|
|
||
| # ex_montecarlo.launch.begin | ||
| # define a function to integrate | ||
| @numba.jit | ||
| def func(x): | ||
| return 1.0 / x | ||
|
|
||
| mc_integrate(1, 2, nsamps) # array(0.6929643, dtype=float32) | ||
| mc_integrate(2, 3, nsamps) # array(0.4054021, dtype=float32) | ||
| # ex_montecarlo.launch.end | ||
|
|
||
| # values computed independently using maple | ||
| np.testing.assert_allclose( | ||
| mc_integrate(1, 2, nsamps), 0.69315, atol=0.001 | ||
| ) | ||
| np.testing.assert_allclose( | ||
| mc_integrate(2, 3, nsamps), 0.4055, atol=0.001 | ||
| ) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.