From 5c87b60f13c7f6c4e417d5e476b333876a41ad04 Mon Sep 17 00:00:00 2001 From: mvyasank Date: Fri, 26 May 2023 14:42:39 +0200 Subject: [PATCH] added perf notebook --- docs/sources/jupyter_notebook.rst | 1 + notebooks/03-mandelbrot_perfomance.ipynb | 872 +++++++++++++++++++++++ 2 files changed, 873 insertions(+) create mode 100644 notebooks/03-mandelbrot_perfomance.ipynb diff --git a/docs/sources/jupyter_notebook.rst b/docs/sources/jupyter_notebook.rst index 6292fd0..882cc7d 100644 --- a/docs/sources/jupyter_notebook.rst +++ b/docs/sources/jupyter_notebook.rst @@ -38,3 +38,4 @@ For more information please refer to `Jupyter documentation Controlling `dpnp` fallback to `numpy` + Examples with performance using Mandelbrot calculation diff --git a/notebooks/03-mandelbrot_perfomance.ipynb b/notebooks/03-mandelbrot_perfomance.ipynb new file mode 100644 index 0000000..edafb84 --- /dev/null +++ b/notebooks/03-mandelbrot_perfomance.ipynb @@ -0,0 +1,872 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "24d4cab0", + "metadata": {}, + "source": [ + "# Examples with performance using Mandelbrot calculation" + ] + }, + { + "cell_type": "markdown", + "id": "e62a1f43", + "metadata": {}, + "source": [ + "[Data Parallel Extensions for Python](https://intelpython.github.io/DPEP/main/) makes calculations on the gpu faster than on the cpu. \n", + "Let's look at performance using the example of the Mandelbrot set computation. \n", + "The Mandelbrot set is the set of points `c` on the complex plane for which the recurrence relation $ z_{n+1} = z_n^2+c $ at \n", + "$ z_0=0 $ defines a bounded sequence. In other words, it is the set of such `c` for which there exists a real `R` such that the inequality \n", + "$ |z_n|" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#calculating the Mandelbrot set on CPU using NumPy* library\n", + "\n", + "#set variables\n", + "DISPLAY_W, DISPLAY_H = 1024, 800\n", + "OFFSET_X = 1.4 * DISPLAY_W // 2\n", + "OFFSET_Y = DISPLAY_H // 2\n", + "ZOOM = 2.5 / DISPLAY_H\n", + "MAX_ITER = 30\n", + "\n", + "#import NumPy* library\n", + "import numpy as np\n", + "#import Matplotlib* library to make visualisation\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline \n", + "\n", + "#create arrays\n", + "c1 = np.asarray([0.0, 0.0, 0.2])\n", + "c2 = np.asarray([1.0, 0.7, 0.9])\n", + "c3 = np.asarray([0.6, 1.0, 0.2])\n", + "\n", + "#perform calculations\n", + "def color_by_intensity(intensity):\n", + " intensity = np.broadcast_to(intensity[:, :, np.newaxis], intensity.shape + (3,))\n", + " return np.where(\n", + " intensity < 0.5,\n", + " c3 * intensity + c2 * (1.0 - intensity),\n", + " c1 * intensity + c2 * (1.0 - intensity),\n", + " )\n", + "\n", + "#implementation of mandelbrot set calculation\n", + "def mandelbrot(w, h, zoom, offset, values):\n", + " x = np.linspace(0, w, num=w, dtype=np.float32)\n", + " y = np.linspace(0, h, num=h, dtype=np.float32)\n", + " xx = (x - offset[0]) * zoom\n", + " yy = (y - offset[1]) * zoom\n", + " c = xx + 1j * yy[:, np.newaxis]\n", + "\n", + " n_iter = np.full(c.shape, 0) # 2d array\n", + " z = np.zeros(c.shape, np.csingle) # 2d array too\n", + " mask = n_iter < MAX_ITER # Initialize with True\n", + " for i in range(MAX_ITER):\n", + " z[mask] = z[mask] ** 2 + c[mask]\n", + " mask = mask & (np.abs(z) <= 2.0)\n", + " n_iter[mask] = i\n", + "\n", + " intensity = n_iter.T / MAX_ITER\n", + " #values = (color_by_intensity(intensity) * 255).astype(np.uint8)\n", + " values = (color_by_intensity(intensity) * 255).astype(np.int32)\n", + " return values\n", + "\n", + "def init_values(w, h):\n", + " return np.full((w, h, 3), 0, dtype=np.int32)\n", + " #return np.full((w, h, 3), 0, dtype=np.uint8)\n", + "\n", + "def asnumpy(values):\n", + " return values\n", + "\n", + "class Fractal:\n", + " def __init__(self, w, h, zoom, offset):\n", + " self.w = w\n", + " self.h = h\n", + " self.values = init_values(w, h)\n", + " self.zoom = zoom\n", + " self.offset = offset\n", + "\n", + " def calculate(self):\n", + " self.values = mandelbrot(self.w, self.h, self.zoom, self.offset, self.values)\n", + "\n", + " def draw(self):\n", + " plt.imshow(self.values)\n", + " \n", + "def main():\n", + " fractal = Fractal(DISPLAY_W, DISPLAY_H, ZOOM, (OFFSET_X, OFFSET_Y))\n", + " #calculating the Mandelbrot set and measuring performance\n", + " %timeit fractal.calculate()\n", + " #draw results\n", + " fractal.draw()\n", + "\n", + "if __name__ == \"__main__\":\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "id": "8dd3214d", + "metadata": {}, + "source": [ + "Now we have the results of code execution on the CPU using the NumPy* library" + ] + }, + { + "cell_type": "markdown", + "id": "4db6b1f0", + "metadata": {}, + "source": [ + "### The Mandelbrot set based on the Data Parallel Extension for NumPy" + ] + }, + { + "cell_type": "markdown", + "id": "2f48cd68", + "metadata": {}, + "source": [ + "To run Python on the GPU, we have to make minor changes to our CPU script, namely:\n", + "1. Changing import statement(s) (call the Data Parallel Extension for NumPy)\n", + "2. Specifying on which device(s) the data is allocated\n", + "3. Explicitly copying data between devices and the host as needed" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e4c3536a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "level_zero:gpu\n", + "947 ms ± 19.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#calculating the Mandelbrot set on GPU using dpnp library\n", + "\n", + "#set variables\n", + "DISPLAY_W, DISPLAY_H = 1024, 800\n", + "OFFSET_X = 1.4 * DISPLAY_W // 2\n", + "OFFSET_Y = DISPLAY_H // 2\n", + "ZOOM = 2.5 / DISPLAY_H\n", + "MAX_ITER = 30\n", + "\n", + "#specify the device type\n", + "import os\n", + "os.environ[\"SYCL_DEVICE_FILTER\"] = \"level_zero:gpu\"\n", + "#os.environ[\"SYCL_DEVICE_FILTER\"] = \"opencl:gpu\"\n", + "#os.environ[\"SYCL_DEVICE_FILTER\"] = \"cpu\"\n", + "print (os.environ[\"SYCL_DEVICE_FILTER\"])\n", + "\n", + "#import dpnp library\n", + "import dpnp as np\n", + "\n", + "#import Matplotlib* library to make visualisation\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline \n", + "\n", + "#create arrays\n", + "c1 = np.asarray([0.0, 0.0, 0.2])\n", + "c2 = np.asarray([1.0, 0.7, 0.9])\n", + "c3 = np.asarray([0.6, 1.0, 0.2])\n", + "\n", + "#perform calculations\n", + "def color_by_intensity(intensity):\n", + " intensity = np.broadcast_to(intensity[:, :, np.newaxis], intensity.shape + (3,))\n", + " return np.where(\n", + " intensity < 0.5,\n", + " c3 * intensity + c2 * (1.0 - intensity),\n", + " c1 * intensity + c2 * (1.0 - intensity),\n", + " )\n", + "\n", + "#implementation of mandelbrot set calculation\n", + "def mandelbrot(w, h, zoom, offset, values):\n", + " x = np.linspace(0, w, num=w, dtype=np.float32)\n", + " y = np.linspace(0, h, num=h, dtype=np.float32)\n", + " xx = (x - offset[0]) * zoom\n", + " yy = (y - offset[1]) * zoom\n", + " c = xx + 1j * yy[:, np.newaxis]\n", + "\n", + " \n", + " n_iter = np.full(c.shape, 0) # 2d array\n", + " z = np.zeros(c.shape, dtype=np.csingle) # 2d array too\n", + "\n", + " mask = n_iter < MAX_ITER # Initialize with True\n", + " for i in range(MAX_ITER):\n", + " z[mask] = z[mask] ** 2 + c[mask]\n", + " mask = mask & (np.abs(z) <= 2.0)\n", + " n_iter[mask] = i\n", + "\n", + " intensity = n_iter.T / MAX_ITER\n", + " values = (color_by_intensity(intensity) * 255).astype(np.int32)\n", + " return values\n", + "\n", + "def init_values(w, h):\n", + " return np.full((w, h, 3), 0, dtype=np.int32)\n", + "\n", + "def asnumpy(values):\n", + " return values\n", + "\n", + "class Fractal:\n", + " def __init__(self, w, h, zoom, offset):\n", + " self.w = w\n", + " self.h = h\n", + " self.values = init_values(w, h)\n", + " self.zoom = zoom\n", + " self.offset = offset\n", + "\n", + " def calculate(self):\n", + " self.values = mandelbrot(self.w, self.h, self.zoom, self.offset, self.values)\n", + "\n", + " def draw(self):\n", + " #return the NumPy* array with input data\n", + " cpu_values = np.asnumpy(self.values)\n", + " plt.imshow(cpu_values)\n", + " \n", + "def main():\n", + " fractal = Fractal(DISPLAY_W, DISPLAY_H, ZOOM, (OFFSET_X, OFFSET_Y))\n", + " #calculating the Mandelbrot set and measuring performance\n", + " %timeit fractal.calculate()\n", + " #draw results\n", + " fractal.draw()\n", + "\n", + "if __name__ == \"__main__\":\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "id": "a0a3b406", + "metadata": {}, + "source": [ + "You can compare the performance of the gpu calculations with the dpnp library using the OpenCL (Open Computing Language) driver. For both type of GPU drivers performance will be 2 times faster than on cpu." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f702fa13", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "opencl:gpu\n", + "967 ms ± 15.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#calculating the Mandelbrot set on OpenCL GPU device using dpnp library\n", + "\n", + "#set variables\n", + "DISPLAY_W, DISPLAY_H = 1024, 800\n", + "OFFSET_X = 1.4 * DISPLAY_W // 2\n", + "OFFSET_Y = DISPLAY_H // 2\n", + "ZOOM = 2.5 / DISPLAY_H\n", + "MAX_ITER = 30\n", + "\n", + "#specify the device type\n", + "import os\n", + "#os.environ[\"SYCL_DEVICE_FILTER\"] = \"level_zero:gpu\"\n", + "os.environ[\"SYCL_DEVICE_FILTER\"] = \"opencl:gpu\"\n", + "#os.environ[\"SYCL_DEVICE_FILTER\"] = \"cpu\"\n", + "print (os.environ[\"SYCL_DEVICE_FILTER\"])\n", + "\n", + "#import dpnp library\n", + "import dpnp as np\n", + "\n", + "#import Matplotlib* library to make visualisation\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline \n", + "\n", + "#create arrays\n", + "c1 = np.asarray([0.0, 0.0, 0.2])\n", + "c2 = np.asarray([1.0, 0.7, 0.9])\n", + "c3 = np.asarray([0.6, 1.0, 0.2])\n", + "\n", + "#perform calculations\n", + "def color_by_intensity(intensity):\n", + " intensity = np.broadcast_to(intensity[:, :, np.newaxis], intensity.shape + (3,))\n", + " return np.where(\n", + " intensity < 0.5,\n", + " c3 * intensity + c2 * (1.0 - intensity),\n", + " c1 * intensity + c2 * (1.0 - intensity),\n", + " )\n", + "\n", + "#implementation of mandelbrot set calculation\n", + "def mandelbrot(w, h, zoom, offset, values):\n", + " x = np.linspace(0, w, num=w, dtype=np.float32)\n", + " y = np.linspace(0, h, num=h, dtype=np.float32)\n", + " xx = (x - offset[0]) * zoom\n", + " yy = (y - offset[1]) * zoom\n", + " c = xx + 1j * yy[:, np.newaxis]\n", + "\n", + " \n", + " n_iter = np.full(c.shape, 0) # 2d array\n", + " z = np.zeros(c.shape, dtype=np.csingle) # 2d array too\n", + "\n", + " mask = n_iter < MAX_ITER # Initialize with True\n", + " for i in range(MAX_ITER):\n", + " z[mask] = z[mask] ** 2 + c[mask]\n", + " mask = mask & (np.abs(z) <= 2.0)\n", + " n_iter[mask] = i\n", + "\n", + " intensity = n_iter.T / MAX_ITER\n", + " values = (color_by_intensity(intensity) * 255).astype(np.int32)\n", + " return values\n", + "\n", + "def init_values(w, h):\n", + " return np.full((w, h, 3), 0, dtype=np.int32)\n", + "\n", + "def asnumpy(values):\n", + " return values\n", + "\n", + "class Fractal:\n", + " def __init__(self, w, h, zoom, offset):\n", + " self.w = w\n", + " self.h = h\n", + " self.values = init_values(w, h)\n", + " self.zoom = zoom\n", + " self.offset = offset\n", + "\n", + " def calculate(self):\n", + " self.values = mandelbrot(self.w, self.h, self.zoom, self.offset, self.values)\n", + "\n", + " def draw(self):\n", + " #return the NumPy* array with input data\n", + " cpu_values = np.asnumpy(self.values)\n", + " plt.imshow(cpu_values)\n", + " \n", + "def main():\n", + " fractal = Fractal(DISPLAY_W, DISPLAY_H, ZOOM, (OFFSET_X, OFFSET_Y))\n", + " #calculating the Mandelbrot set and measuring performance\n", + " %timeit fractal.calculate()\n", + " #draw results\n", + " fractal.draw()\n", + "\n", + "if __name__ == \"__main__\":\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "id": "ac61f656", + "metadata": {}, + "source": [ + "Calculations using the Data Parallel Extension for NumPy on the GPU will be faster than the same calculations using the same library on the CPU. Lets compare this." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "0c2a4f2d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cpu\n", + "908 ms ± 9.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#calculating the Mandelbrot set on CPU device using dpnp library\n", + "\n", + "#set variables\n", + "DISPLAY_W, DISPLAY_H = 1024, 800\n", + "OFFSET_X = 1.4 * DISPLAY_W // 2\n", + "OFFSET_Y = DISPLAY_H // 2\n", + "ZOOM = 2.5 / DISPLAY_H\n", + "MAX_ITER = 30\n", + "\n", + "#specify the device type\n", + "import os\n", + "#os.environ[\"SYCL_DEVICE_FILTER\"] = \"level_zero:gpu\"\n", + "#os.environ[\"SYCL_DEVICE_FILTER\"] = \"opencl:gpu\"\n", + "os.environ[\"SYCL_DEVICE_FILTER\"] = \"cpu\"\n", + "print (os.environ[\"SYCL_DEVICE_FILTER\"])\n", + "\n", + "#import dpnp library\n", + "import dpnp as np\n", + "\n", + "#import Matplotlib* library to make visualisation\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline \n", + "\n", + "#create arrays\n", + "c1 = np.asarray([0.0, 0.0, 0.2])\n", + "c2 = np.asarray([1.0, 0.7, 0.9])\n", + "c3 = np.asarray([0.6, 1.0, 0.2])\n", + "\n", + "#perform calculations\n", + "def color_by_intensity(intensity):\n", + " intensity = np.broadcast_to(intensity[:, :, np.newaxis], intensity.shape + (3,))\n", + " return np.where(\n", + " intensity < 0.5,\n", + " c3 * intensity + c2 * (1.0 - intensity),\n", + " c1 * intensity + c2 * (1.0 - intensity),\n", + " )\n", + "\n", + "#implementation of mandelbrot set calculation\n", + "def mandelbrot(w, h, zoom, offset, values):\n", + " x = np.linspace(0, w, num=w, dtype=np.float32)\n", + " y = np.linspace(0, h, num=h, dtype=np.float32)\n", + " xx = (x - offset[0]) * zoom\n", + " yy = (y - offset[1]) * zoom\n", + " c = xx + 1j * yy[:, np.newaxis]\n", + "\n", + " \n", + " n_iter = np.full(c.shape, 0) # 2d array\n", + " z = np.zeros(c.shape, dtype=np.csingle) # 2d array too\n", + "\n", + " mask = n_iter < MAX_ITER # Initialize with True\n", + " for i in range(MAX_ITER):\n", + " z[mask] = z[mask] ** 2 + c[mask]\n", + " mask = mask & (np.abs(z) <= 2.0)\n", + " n_iter[mask] = i\n", + "\n", + " intensity = n_iter.T / MAX_ITER\n", + " values = (color_by_intensity(intensity) * 255).astype(np.int32)\n", + " return values\n", + "\n", + "def init_values(w, h):\n", + " return np.full((w, h, 3), 0, dtype=np.int32)\n", + "\n", + "def asnumpy(values):\n", + " return values\n", + "\n", + "class Fractal:\n", + " def __init__(self, w, h, zoom, offset):\n", + " self.w = w\n", + " self.h = h\n", + " self.values = init_values(w, h)\n", + " self.zoom = zoom\n", + " self.offset = offset\n", + "\n", + " def calculate(self):\n", + " self.values = mandelbrot(self.w, self.h, self.zoom, self.offset, self.values)\n", + "\n", + " def draw(self):\n", + " #return the NumPy* array with input data\n", + " cpu_values = np.asnumpy(self.values)\n", + " plt.imshow(cpu_values)\n", + " \n", + "def main():\n", + " fractal = Fractal(DISPLAY_W, DISPLAY_H, ZOOM, (OFFSET_X, OFFSET_Y))\n", + " #calculating the Mandelbrot set and measuring performance\n", + " %timeit fractal.calculate()\n", + " #draw results\n", + " fractal.draw()\n", + "\n", + "if __name__ == \"__main__\":\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "id": "ba97cbfd", + "metadata": {}, + "source": [ + "### The Mandelbrot set on the Numba" + ] + }, + { + "cell_type": "markdown", + "id": "0a314c05", + "metadata": {}, + "source": [ + "If we run the Mandelbrot set on Numba, the high-performance Python compiler, we see that the computation on it is faster than on the Numpy library." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f1e11ebc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7.23 ms ± 875 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#calculating the Mandelbrot set on CPU device using dpnp library\n", + "\n", + "#set variables\n", + "DISPLAY_W, DISPLAY_H = 1024, 800\n", + "OFFSET_X = 1.4 * DISPLAY_W // 2\n", + "OFFSET_Y = DISPLAY_H // 2\n", + "ZOOM = 2.5 / DISPLAY_H\n", + "MAX_ITER = 30\n", + "\n", + "#import libraries\n", + "import numpy as np\n", + "import numba as nb\n", + "\n", + "#import Matplotlib* library to make visualisation\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline \n", + "\n", + "nb.config.THREADING_LAYER = \"omp\"\n", + "\n", + "#perform calculations\n", + "@nb.jit(fastmath=True, nopython=True)\n", + "def color_by_intensity(intensity, c1, c2, c3):\n", + " if intensity < 0.5:\n", + " return c3 * intensity + c2 * (1.0 - intensity)\n", + " else:\n", + " return c1 * intensity + c2 * (1.0 - intensity)\n", + "\n", + "@nb.jit(fastmath=True, nopython=True)\n", + "def mandel(x, y):\n", + " c = complex(x, y)\n", + " z = 0.0j\n", + " for i in range(MAX_ITER):\n", + " z = z * z + c\n", + " if (z.real * z.real + z.imag * z.imag) > 4.0:\n", + " return i\n", + " return MAX_ITER\n", + "\n", + "#implementation of mandelbrot set calculation\n", + "@nb.jit(fastmath=True, nopython=True, parallel=True)\n", + "def mandelbrot(w, h, zoom, offset, values):\n", + " c1 = np.asarray([0.0, 0.0, 0.2])\n", + " c2 = np.asarray([1.0, 0.7, 0.9])\n", + " c3 = np.asarray([0.6, 1.0, 0.2])\n", + "\n", + " for x in nb.prange(w):\n", + " for y in range(h):\n", + " xx = (x - offset[0]) * zoom\n", + " yy = (y - offset[1]) * zoom\n", + " intensity = mandel(xx, yy) / MAX_ITER\n", + " for c in range(3):\n", + " color = color_by_intensity(intensity, c1[c], c2[c], c3[c])\n", + " color = int(color * 255.0)\n", + " values[x, y, c] = color\n", + " return values\n", + "\n", + "def init_values(w, h):\n", + " return np.full((w, h, 3), 0, dtype=np.int32)\n", + "\n", + "def asnumpy(values):\n", + " return values\n", + "\n", + "class Fractal:\n", + " def __init__(self, w, h, zoom, offset):\n", + " self.w = w\n", + " self.h = h\n", + " self.values = init_values(w, h)\n", + " self.zoom = zoom\n", + " self.offset = offset\n", + "\n", + " def calculate(self):\n", + " self.values = mandelbrot(self.w, self.h, self.zoom, self.offset, self.values)\n", + "\n", + " def draw(self):\n", + " plt.imshow(self.values)\n", + "\n", + "def main():\n", + " fractal = Fractal(DISPLAY_W, DISPLAY_H, ZOOM, (OFFSET_X, OFFSET_Y))\n", + " #calculating the Mandelbrot set and measuring performance\n", + " %timeit fractal.calculate()\n", + " #draw results\n", + " fractal.draw()\n", + "\n", + "if __name__ == \"__main__\":\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "id": "9d5d93ef", + "metadata": {}, + "source": [ + "### The Mandelbrot set on the Data Parallel Extension for Numba" + ] + }, + { + "cell_type": "markdown", + "id": "6c03c2c9", + "metadata": {}, + "source": [ + "The calculation on the Data Parallel Extension for Numba go faster than on the Numba or on the Data Parallel Extension for NumPy." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1c45197f", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\mvyasank\\Anaconda3\\envs\\my_env\\lib\\site-packages\\numba_dpex\\decorators.py:154: RuntimeWarning: nopython is set for dpjit and is ignored\n", + " warnings.warn(\n" + ] + }, + { + "ename": "TypingError", + "evalue": "Failed in dpex_dpjit_nopython mode pipeline (step: nopython frontend)\n\u001b[1m\u001b[1mUnknown attribute 'asarray' of type Module()\n\u001b[1m\nFile \"AppData\\Local\\Temp\\ipykernel_29672\\2269767202.py\", line 41:\u001b[0m\n\u001b[1m\u001b[0m\n\u001b[0m\n\u001b[0m\u001b[1mDuring: typing of get attribute at C:\\Users\\mvyasank\\AppData\\Local\\Temp\\ipykernel_29672\\2269767202.py (41)\u001b[0m\n\u001b[1m\nFile \"AppData\\Local\\Temp\\ipykernel_29672\\2269767202.py\", line 41:\u001b[0m\n\u001b[1m\u001b[0m\n", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypingError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[4], line 89\u001b[0m\n\u001b[0;32m 86\u001b[0m fractal\u001b[38;5;241m.\u001b[39mdraw()\n\u001b[0;32m 88\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m__main__\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m---> 89\u001b[0m \u001b[43mmain\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[1;32mIn[4], line 84\u001b[0m, in \u001b[0;36mmain\u001b[1;34m()\u001b[0m\n\u001b[0;32m 82\u001b[0m fractal \u001b[38;5;241m=\u001b[39m Fractal(DISPLAY_W, DISPLAY_H, ZOOM, (OFFSET_X, OFFSET_Y))\n\u001b[0;32m 83\u001b[0m \u001b[38;5;66;03m#calculating the Mandelbrot set and measuring performance\u001b[39;00m\n\u001b[1;32m---> 84\u001b[0m \u001b[43mget_ipython\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun_line_magic\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mtimeit\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mfractal.calculate()\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 85\u001b[0m \u001b[38;5;66;03m#draw results\u001b[39;00m\n\u001b[0;32m 86\u001b[0m fractal\u001b[38;5;241m.\u001b[39mdraw()\n", + "File \u001b[1;32m~\\Anaconda3\\envs\\my_env\\lib\\site-packages\\IPython\\core\\interactiveshell.py:2414\u001b[0m, in \u001b[0;36mInteractiveShell.run_line_magic\u001b[1;34m(self, magic_name, line, _stack_depth)\u001b[0m\n\u001b[0;32m 2412\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlocal_ns\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_local_scope(stack_depth)\n\u001b[0;32m 2413\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbuiltin_trap:\n\u001b[1;32m-> 2414\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 2416\u001b[0m \u001b[38;5;66;03m# The code below prevents the output from being displayed\u001b[39;00m\n\u001b[0;32m 2417\u001b[0m \u001b[38;5;66;03m# when using magics with decodator @output_can_be_silenced\u001b[39;00m\n\u001b[0;32m 2418\u001b[0m \u001b[38;5;66;03m# when the last Python token in the expression is a ';'.\u001b[39;00m\n\u001b[0;32m 2419\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mgetattr\u001b[39m(fn, magic\u001b[38;5;241m.\u001b[39mMAGIC_OUTPUT_CAN_BE_SILENCED, \u001b[38;5;28;01mFalse\u001b[39;00m):\n", + "File \u001b[1;32m~\\Anaconda3\\envs\\my_env\\lib\\site-packages\\IPython\\core\\magics\\execution.py:1170\u001b[0m, in \u001b[0;36mExecutionMagics.timeit\u001b[1;34m(self, line, cell, local_ns)\u001b[0m\n\u001b[0;32m 1168\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m index \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m10\u001b[39m):\n\u001b[0;32m 1169\u001b[0m number \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m10\u001b[39m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m index\n\u001b[1;32m-> 1170\u001b[0m time_number \u001b[38;5;241m=\u001b[39m \u001b[43mtimer\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtimeit\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnumber\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1171\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m time_number \u001b[38;5;241m>\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0.2\u001b[39m:\n\u001b[0;32m 1172\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n", + "File \u001b[1;32m~\\Anaconda3\\envs\\my_env\\lib\\site-packages\\IPython\\core\\magics\\execution.py:158\u001b[0m, in \u001b[0;36mTimer.timeit\u001b[1;34m(self, number)\u001b[0m\n\u001b[0;32m 156\u001b[0m gc\u001b[38;5;241m.\u001b[39mdisable()\n\u001b[0;32m 157\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 158\u001b[0m timing \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mit\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtimer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 159\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 160\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m gcold:\n", + "File \u001b[1;32m:1\u001b[0m, in \u001b[0;36minner\u001b[1;34m(_it, _timer)\u001b[0m\n", + "Cell \u001b[1;32mIn[4], line 74\u001b[0m, in \u001b[0;36mFractal.calculate\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 73\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcalculate\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m---> 74\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalues \u001b[38;5;241m=\u001b[39m \u001b[43mmandelbrot\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mw\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mh\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mzoom\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moffset\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalues\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32m~\\Anaconda3\\envs\\my_env\\lib\\site-packages\\numba\\core\\dispatcher.py:468\u001b[0m, in \u001b[0;36m_DispatcherBase._compile_for_args\u001b[1;34m(self, *args, **kws)\u001b[0m\n\u001b[0;32m 464\u001b[0m msg \u001b[38;5;241m=\u001b[39m (\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mstr\u001b[39m(e)\u001b[38;5;241m.\u001b[39mrstrip()\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mThis error may have been caused \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 465\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mby the following argument(s):\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00margs_str\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 466\u001b[0m e\u001b[38;5;241m.\u001b[39mpatch_message(msg)\n\u001b[1;32m--> 468\u001b[0m \u001b[43merror_rewrite\u001b[49m\u001b[43m(\u001b[49m\u001b[43me\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mtyping\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 469\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m errors\u001b[38;5;241m.\u001b[39mUnsupportedError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m 470\u001b[0m \u001b[38;5;66;03m# Something unsupported is present in the user code, add help info\u001b[39;00m\n\u001b[0;32m 471\u001b[0m error_rewrite(e, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124munsupported_error\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "File \u001b[1;32m~\\Anaconda3\\envs\\my_env\\lib\\site-packages\\numba\\core\\dispatcher.py:409\u001b[0m, in \u001b[0;36m_DispatcherBase._compile_for_args..error_rewrite\u001b[1;34m(e, issue_type)\u001b[0m\n\u001b[0;32m 407\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m e\n\u001b[0;32m 408\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m--> 409\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m e\u001b[38;5;241m.\u001b[39mwith_traceback(\u001b[38;5;28;01mNone\u001b[39;00m)\n", + "\u001b[1;31mTypingError\u001b[0m: Failed in dpex_dpjit_nopython mode pipeline (step: nopython frontend)\n\u001b[1m\u001b[1mUnknown attribute 'asarray' of type Module()\n\u001b[1m\nFile \"AppData\\Local\\Temp\\ipykernel_29672\\2269767202.py\", line 41:\u001b[0m\n\u001b[1m\u001b[0m\n\u001b[0m\n\u001b[0m\u001b[1mDuring: typing of get attribute at C:\\Users\\mvyasank\\AppData\\Local\\Temp\\ipykernel_29672\\2269767202.py (41)\u001b[0m\n\u001b[1m\nFile \"AppData\\Local\\Temp\\ipykernel_29672\\2269767202.py\", line 41:\u001b[0m\n\u001b[1m\u001b[0m\n" + ] + } + ], + "source": [ + "#calculating the Mandelbrot set on CPU device using dpnp library\n", + "\n", + "#set variables\n", + "DISPLAY_W, DISPLAY_H = 1024, 800\n", + "OFFSET_X = 1.4 * DISPLAY_W // 2\n", + "OFFSET_Y = DISPLAY_H // 2\n", + "ZOOM = 2.5 / DISPLAY_H\n", + "MAX_ITER = 30\n", + "\n", + "#import libraries\n", + "import dpnp as np\n", + "import numba_dpex as nb\n", + "\n", + "#import Matplotlib* library to make visualisation\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline \n", + "\n", + "nb.config.THREADING_LAYER = \"omp\"\n", + "\n", + "#perform calculations\n", + "@nb.dpjit(fastmath=True, nopython=True)\n", + "def color_by_intensity(intensity, c1, c2, c3):\n", + " if intensity < 0.5:\n", + " return c3 * intensity + c2 * (1.0 - intensity)\n", + " else:\n", + " return c1 * intensity + c2 * (1.0 - intensity)\n", + "\n", + "@nb.dpjit(fastmath=True, nopython=True)\n", + "def mandel(x, y):\n", + " c = complex(x, y)\n", + " z = 0.0j\n", + " for i in range(MAX_ITER):\n", + " z = z * z + c\n", + " if (z.real * z.real + z.imag * z.imag) > 4.0:\n", + " return i\n", + " return MAX_ITER\n", + "\n", + "#implementation of mandelbrot set calculation\n", + "@nb.dpjit(fastmath=True, nopython=True, parallel=True)\n", + "def mandelbrot(w, h, zoom, offset, values):\n", + " c1 = np.asarray([0.0, 0.0, 0.2])\n", + " c2 = np.asarray([1.0, 0.7, 0.9])\n", + " c3 = np.asarray([0.6, 1.0, 0.2])\n", + "\n", + " for x in nb.prange(w):\n", + " for y in range(h):\n", + " xx = (x - offset[0]) * zoom\n", + " yy = (y - offset[1]) * zoom\n", + " intensity = mandel(xx, yy) / MAX_ITER\n", + " for c in range(3):\n", + " color = color_by_intensity(intensity, c1[c], c2[c], c3[c])\n", + " color = int(color * 255.0)\n", + " values[x, y, c] = color\n", + " return values\n", + "\n", + "def init_values(w, h):\n", + " return np.full((w, h, 3), 0, dtype=np.int32)\n", + "\n", + "def asnumpy(values):\n", + " return values\n", + "\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline \n", + "\n", + "class Fractal:\n", + " def __init__(self, w, h, zoom, offset):\n", + " self.w = w\n", + " self.h = h\n", + " self.values = init_values(w, h)\n", + " self.zoom = zoom\n", + " self.offset = offset\n", + "\n", + " def calculate(self):\n", + " self.values = mandelbrot(self.w, self.h, self.zoom, self.offset, self.values)\n", + "\n", + " def draw(self):\n", + " #return the NumPy* array with input data\n", + " cpu_values = np.asnumpy(self.values)\n", + " plt.imshow(cpu_values)\n", + " \n", + "def main():\n", + " fractal = Fractal(DISPLAY_W, DISPLAY_H, ZOOM, (OFFSET_X, OFFSET_Y))\n", + " #calculating the Mandelbrot set and measuring performance\n", + " %timeit fractal.calculate()\n", + " #draw results\n", + " fractal.draw()\n", + "\n", + "if __name__ == \"__main__\":\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "id": "e9c275ff", + "metadata": {}, + "source": [ + "### The conclusion" + ] + }, + { + "cell_type": "markdown", + "id": "e5fe4a54", + "metadata": {}, + "source": [ + "The Data Parallel Extension for Python libraries follow the \"compute follows data\" approach, with all data being created on the same device where all the computation takes place.\n", + "\n", + "Based on the experiment with Mandelbrot calculation we see the folliwng:\n", + "\n", + "| Results |\n", + "| :------------- |\n", + "| The NumPy* library shows the slowest results |\n", + "| The Data Parallel Extension for NumPy is faster than the NumPy* library |\n", + "| The Data Parallel Extension for NumPy on the GPU = the NumPy* library * 0,8 |\n", + "| The Data Parallel Extension for NumPy on the GPU = The Data Parallel Extension for NumPy on the CPU * 0,5 |\n", + "| The Numba is faster than the NumPy* library |\n", + "| The Data Parallel Extension for Numba is faster than the Numba and The Data Parallel Extension for NumPy |\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}