Skip to content

Instantly share code, notes, and snippets.

@gwbischof
Created December 6, 2019 01:02
Show Gist options
  • Select an option

  • Save gwbischof/b17d1ba3b1d60c5ecbffabc22747880b to your computer and use it in GitHub Desktop.

Select an option

Save gwbischof/b17d1ba3b1d60c5ecbffabc22747880b to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Code optimization\n",
"## Comarison of execution time for native python, numba compiled, and gpu code.\n",
"\n",
"Below we define 3 functions. All three functions increment each element of an array by 1.\n",
"- The first function is a standard python function.\n",
"- The second is a numba compiled version of this function. The only thing you need to do this is jit decorator.\n",
"- The third function is compiled for the GPU. This code needs to be written a bit differently. Each thread on the GPU executes the entire function."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"import numba\n",
"from numba import cuda\n",
"from numba import int64\n",
"import numpy\n",
"\n",
"def test(a):\n",
" for i in range(a.size):\n",
" a[i] += 1 \n",
"\n",
"# Using the numba.jit decorator means that when this function is first called it is optimized and compiled.\n",
"# Same function as the previous one, just added the decorator.\n",
"@numba.jit\n",
"def test_jit(a):\n",
" for i in range(a.size):\n",
" a[i] += 1\n",
" \n",
"# numba.prange singals that iterations of the loop can be executed independantly.\n",
"@numba.jit(nopython=True, parallel=True)\n",
"def test_jit_parallel(a):\n",
" for i in numba.prange(a.size):\n",
" a[i] += 1\n",
"\n",
"# The same program but implemented for the GPU.\n",
"@cuda.jit\n",
"def test_gpu(a):\n",
" b = 0\n",
" pos = cuda.grid(1)\n",
" if pos < a.size:\n",
" b = a[pos] + 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Use timeit to check performance of the three functions. Array size is 1000"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1000 element array\n",
"increment array normal\n",
"481 µs ± 10.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n",
"increment array compiled CPU\n",
"1.3 µs ± 19.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n",
"increment array compiled CPU parallel\n",
"53.1 µs ± 1.71 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n",
"increment array GPU\n",
"465 µs ± 8.39 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n",
"increment array GPU preload data\n",
"161 µs ± 12.7 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n"
]
}
],
"source": [
"print(\"1000 element array\")\n",
"print(\"increment array normal\")\n",
"a = numpy.arange(1000)\n",
"%timeit test(a)\n",
"\n",
"print(\"increment array compiled CPU\")\n",
"a = numpy.arange(1000)\n",
"%timeit test_jit(a)\n",
"\n",
"print(\"increment array compiled CPU parallel\")\n",
"a = numpy.arange(1000)\n",
"%timeit test_jit_parallel(a)\n",
"\n",
"print(\"increment array GPU\")\n",
"a = numpy.arange(1000)\n",
"threadsperblock = 32\n",
"blockspergrid = (a.size + (threadsperblock - 1)) // threadsperblock\n",
"%timeit test_gpu[blockspergrid, threadsperblock](a)\n",
"\n",
"print(\"increment array GPU preload data\")\n",
"a = numpy.arange(1000)\n",
"d_arr = cuda.to_device(a)\n",
"threadsperblock = 32\n",
"blockspergrid = (a.size + (threadsperblock - 1)) // threadsperblock\n",
"%timeit test_gpu[blockspergrid, threadsperblock](d_arr)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Use timeit to check performance of the three functions. Array size is 1000000"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1000000 element array\n",
"increment array normal\n",
"504 ms ± 16.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
"increment array compiled CPU\n",
"493 µs ± 9.22 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n",
"increment array compiled CPU parallel\n",
"52.3 µs ± 2.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n",
"increment array GPU\n",
"4.26 ms ± 74 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
"increment array GPU preload data\n",
"150 µs ± 2.98 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n"
]
}
],
"source": [
"print(\"1000000 element array\")\n",
"print(\"increment array normal\")\n",
"a = numpy.arange(1000000)\n",
"%timeit test(a)\n",
"\n",
"print(\"increment array compiled CPU\")\n",
"a = numpy.arange(1000000)\n",
"%timeit test_jit(a)\n",
"\n",
"print(\"increment array compiled CPU parallel\")\n",
"a = numpy.arange(1000)\n",
"%timeit test_jit_parallel(a)\n",
"\n",
"print(\"increment array GPU\")\n",
"a = numpy.arange(1000000)\n",
"threadsperblock = 32\n",
"blockspergrid = (a.size + (threadsperblock - 1)) // threadsperblock\n",
"\n",
"%timeit test_gpu[blockspergrid, threadsperblock](a)\n",
"print(\"increment array GPU preload data\")\n",
"a = numpy.arange(1000000)\n",
"d_arr = cuda.to_device(a)\n",
"threadsperblock = 32\n",
"blockspergrid = (a.size + (threadsperblock - 1)) // threadsperblock\n",
"%timeit test_gpu[blockspergrid, threadsperblock](d_arr)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Repeating the same process as above except with different functions."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"import numba\n",
"from numba import cuda\n",
"import numpy\n",
"import math\n",
"\n",
"def test2(a):\n",
" for i in range(a.size):\n",
" a[i] *= a[i]\n",
"\n",
"@numba.jit\n",
"def test2_jit(a):\n",
" for i in range(a.size):\n",
" a[i] *= a[i]\n",
" \n",
"@cuda.jit\n",
"def test2_gpu(a):\n",
" b=0\n",
" pos = cuda.grid(1)\n",
" if pos < a.size:\n",
" b = a[pos] * a[pos]"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1000 element array\n",
"square array normal\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/gbischof/anaconda3/envs/garrett2/lib/python3.7/site-packages/ipykernel_launcher.py:8: RuntimeWarning: overflow encountered in long_scalars\n",
" \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"231 µs ± 3.67 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n",
"square array compiled CPU\n",
"293 ns ± 3.16 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n",
"square array GPU\n",
"445 µs ± 3.11 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n",
"increment array GPU preload data\n",
"74.7 µs ± 262 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n"
]
}
],
"source": [
"print(\"1000 element array\")\n",
"print(\"square array normal\")\n",
"a = numpy.arange(1000)\n",
"%timeit test2(a)\n",
"\n",
"print(\"square array compiled CPU\")\n",
"a = numpy.arange(1000)\n",
"%timeit test2_jit(a)\n",
"\n",
"print(\"square array GPU\")\n",
"a = numpy.arange(1000)\n",
"threadsperblock = 32\n",
"blockspergrid = (a.size + (threadsperblock - 1)) // threadsperblock\n",
"%timeit test2_gpu[blockspergrid, threadsperblock](a)\n",
"\n",
"print(\"increment array GPU preload data\")\n",
"a = numpy.arange(1000)\n",
"d_arr = cuda.to_device(a)\n",
"threadsperblock = 32\n",
"blockspergrid = (a.size + (threadsperblock - 1)) // threadsperblock\n",
"%timeit test2_gpu[blockspergrid, threadsperblock](d_arr)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1000000 element array\n",
"square array normal\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/gbischof/anaconda3/envs/garrett2/lib/python3.7/site-packages/ipykernel_launcher.py:8: RuntimeWarning: overflow encountered in long_scalars\n",
" \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"591 ms ± 161 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
"square array compiled CPU\n",
"214 µs ± 4.98 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n",
"square array GPU\n",
"2.35 ms ± 9.01 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
"increment array GPU preload data\n",
"74.4 µs ± 347 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n"
]
}
],
"source": [
"print(\"1000000 element array\")\n",
"print(\"square array normal\")\n",
"a = numpy.arange(1000000)\n",
"%timeit test2(a)\n",
"\n",
"print(\"square array compiled CPU\")\n",
"a = numpy.arange(1000000)\n",
"%timeit test2_jit(a)\n",
"\n",
"print(\"square array GPU\")\n",
"a = numpy.arange(1000000)\n",
"threadsperblock = 32\n",
"blockspergrid = (a.size + (threadsperblock - 1)) // threadsperblock\n",
"%timeit test2_gpu[blockspergrid, threadsperblock](a)\n",
"\n",
"print(\"increment array GPU preload data\")\n",
"a = numpy.arange(1000000)\n",
"d_arr = cuda.to_device(a)\n",
"threadsperblock = 32\n",
"blockspergrid = (a.size + (threadsperblock - 1)) // threadsperblock\n",
"%timeit test2_gpu[blockspergrid, threadsperblock](d_arr)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment