Created
December 6, 2019 01:02
-
-
Save gwbischof/b17d1ba3b1d60c5ecbffabc22747880b to your computer and use it in GitHub Desktop.
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
| { | |
| "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