├── .gitignore ├── 1_interpreter.pdf ├── Dask_examples.ipynb ├── LICENSE.md ├── Polars_vs_FireDucks_vs_cuDF_vs_Pandas.ipynb ├── README.md ├── Vaex_examples.ipynb ├── pandas_speedup.ipynb ├── polars_pandas2.ipynb ├── python_interpreters.ipynb └── speedingloops.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | archive/* 3 | archive 4 | -------------------------------------------------------------------------------- /1_interpreter.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huqy/HighPerfDataSciPython/c07028d1952bce824ef985da3f38fa686d8f739c/1_interpreter.pdf -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Qiyang Hu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UCLA OARC/IDRE Workshop Series "Python for High Performance Data Analytics" 2 | 3 | [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://huqy.github.io/HighPerfDataSciPython/LICENSE.md) 4 | 5 | While Python has been the most popular programming language since 2019, data scientists often critique its slow speed and limited capabilities in handling big data scenarios. In this workshop series, we’ll tackle how to enhance Python’s performance in data science by diving deep into its workings and leveraging technologies to transform Python into an effective tool for high-performance big data analytics. 6 | 7 | This folder contains the PDF slides and colab demos for the "Python for High Performance Data Analytics" workshop series hosted by UCLA OARC. 8 | 9 | The lecture contents are currently updated for Feb 2025. 10 | 11 | #### (1) Computation 12 | 13 | - When: Feb 21, 2025 10am-12:00pm (PST) 14 | - Please [register to the Zoom meeting](https://ucla.zoom.us/meeting/register/tJAsduGopjIuGdY_Axzz8p-4S0a5GsmaJW0t) before joining 15 | - [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](http://bit.ly/hpdspy_01) – for Python Interpreter demo 16 | - [Slides](https://huqy.github.io/HighPerfDataSciPython/1_interpreter.pdf) 17 | - [Survey](https://app.smartsheet.com/b/form/90289a7a99904ef395c7bebbb7c9f212) 18 | 19 | 20 | #### (2) Array-Type Data 21 | 22 | - When: Feb 28, 2025 10am-12:00pm (PST) 23 | - Please [register to the Zoom meeting](https://ucla.zoom.us/meeting/register/tJMvdO2tqzgvGdOHXgn4alVu04ckveI1ZFif#/registration) before joining 24 | - [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](http://bit.ly/hpdspy_00) – for Looping speedup tricks demo 25 | - [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](http://bit.ly/hpdspy_02) – for NumPy/Pandas speedup demo 26 | - [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](http://bit.ly/hpdspy_03) – for Dask examples demo 27 | - [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](http://bit.ly/hpdspy_04) – for Vaex examples demo 28 | - [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](http://bit.ly/hpdspy_05) – for Pandas/Polars examples demo 29 | - [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](http://bit.ly/hpdspy_06) – for Pandas/Polars/Fireducks/cuDF examples demo 30 | - Slides 31 | - [Survey](https://app.smartsheet.com/b/form/90289a7a99904ef395c7bebbb7c9f212) 32 | 33 | ### Instructor: [Qiyang Hu](https://oarc.ucla.edu/people/profiles/qiyang-hu) 34 | -------------------------------------------------------------------------------- /python_interpreters.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "kernelspec": { 6 | "display_name": "Python [default]", 7 | "language": "python", 8 | "name": "python3" 9 | }, 10 | "language_info": { 11 | "codemirror_mode": { 12 | "name": "ipython", 13 | "version": 3 14 | }, 15 | "file_extension": ".py", 16 | "mimetype": "text/x-python", 17 | "name": "python", 18 | "nbconvert_exporter": "python", 19 | "pygments_lexer": "ipython3", 20 | "version": "3.5.1" 21 | }, 22 | "colab": { 23 | "name": "python_interpreters.ipynb", 24 | "provenance": [], 25 | "collapsed_sections": [], 26 | "toc_visible": true 27 | } 28 | }, 29 | "cells": [ 30 | { 31 | "cell_type": "markdown", 32 | "metadata": { 33 | "id": "5s0SVEflOppl" 34 | }, 35 | "source": [ 36 | "# Python Interpreter Usage and Performance Study" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": { 42 | "id": "k2xAw8CgZsON" 43 | }, 44 | "source": [ 45 | "##### Copyright 2021 Qiyang Hu" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "metadata": { 51 | "id": "1OTDr0wiZvhO" 52 | }, 53 | "source": [ 54 | "#@title Licensed under MIT License (the \"License\");\n", 55 | "# you may not use this file except in compliance with the License.\n", 56 | "# You may obtain a copy of the License at\n", 57 | "#\n", 58 | "# https://huqy.github.io/HighPerfDataSciPython/LICENSE.md\n", 59 | "#\n", 60 | "# Unless required by applicable law or agreed to in writing, software\n", 61 | "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", 62 | "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", 63 | "# See the License for the specific language governing permissions and\n", 64 | "# limitations under the License." 65 | ], 66 | "execution_count": null, 67 | "outputs": [] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": { 72 | "id": "Jk2STeZmOpps" 73 | }, 74 | "source": [ 75 | "*Basic idea of this notebook was initiated by Jake Vanderplas's*\n", 76 | "[*post*](http://jakevdp.github.io/blog/2012/08/24/numba-vs-cython/)\n", 77 | "*on his blog*\n", 78 | "[*Pythonic Perambulations*](http://jakevdp.github.io)\n", 79 | "*and I expanded it for illustrating the content of our lecture.*\n" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": { 85 | "id": "Ec-sz98qOpps" 86 | }, 87 | "source": [ 88 | "All examples were implemented to run a **pairwise distance** function, which takes an array representing \n", 89 | "``M`` points in ``N`` dimensions, and return the ``M x M`` matrix of pairwise distances.\n", 90 | "This is a nice test function for a few reasons: \n", 91 | "\n", 92 | "\n", 93 | "\n", 94 | "* It's a very clean and well-defined test.\n", 95 | "* It illustrates the kind of array-based operation that is common in statistics, datamining, and machine learning.\n", 96 | "* It is a function that results in large memory consumption if the standard numpy broadcasting approach is used\n", 97 | "(it requires a temporary array containing ``M * M * N`` elements), making it a good\n", 98 | "candidate for an alternate approach." 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": { 104 | "id": "igpNb_oYftBM" 105 | }, 106 | "source": [ 107 | "## Definition of the array" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "metadata": { 113 | "id": "ET_56GPzOppt" 114 | }, 115 | "source": [ 116 | "import numpy as np\n", 117 | "X = np.random.random((1000, 3))" 118 | ], 119 | "execution_count": null, 120 | "outputs": [] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "metadata": { 125 | "colab": { 126 | "base_uri": "https://localhost:8080/" 127 | }, 128 | "id": "7O3nsp-bQotV", 129 | "outputId": "31c87d47-6986-4669-e546-de563985abce" 130 | }, 131 | "source": [ 132 | "X" 133 | ], 134 | "execution_count": null, 135 | "outputs": [ 136 | { 137 | "output_type": "execute_result", 138 | "data": { 139 | "text/plain": [ 140 | "array([[0.62247839, 0.78616676, 0.5389317 ],\n", 141 | " [0.74208568, 0.11986852, 0.68971587],\n", 142 | " [0.01789765, 0.99036013, 0.50165867],\n", 143 | " ...,\n", 144 | " [0.00906431, 0.07135082, 0.55711218],\n", 145 | " [0.00929381, 0.86259518, 0.39042095],\n", 146 | " [0.36758103, 0.5823112 , 0.36154291]])" 147 | ] 148 | }, 149 | "metadata": { 150 | "tags": [] 151 | }, 152 | "execution_count": 3 153 | } 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": { 159 | "id": "-8r_tt9nOppv" 160 | }, 161 | "source": [ 162 | "## Pure Python Function" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": { 168 | "id": "i4g0sTevOppw" 169 | }, 170 | "source": [ 171 | "A loop-based solution avoids the overhead associated with temporary arrays,\n", 172 | "and can be written like this:" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "metadata": { 178 | "id": "h1iqLBFxOppw" 179 | }, 180 | "source": [ 181 | "def pairwise_python(X):\n", 182 | " M = X.shape[0]\n", 183 | " N = X.shape[1]\n", 184 | " D = np.empty((M, M), dtype=np.float)\n", 185 | " for i in range(M):\n", 186 | " for j in range(M):\n", 187 | " d = 0.0\n", 188 | " for k in range(N):\n", 189 | " tmp = X[i, k] - X[j, k]\n", 190 | " d += tmp * tmp\n", 191 | " D[i, j] = np.sqrt(d)\n", 192 | " return D" 193 | ], 194 | "execution_count": null, 195 | "outputs": [] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "metadata": { 200 | "colab": { 201 | "base_uri": "https://localhost:8080/" 202 | }, 203 | "id": "u_oqBBpCpGNM", 204 | "outputId": "58274bb3-2029-459a-f961-3bc2d8846734" 205 | }, 206 | "source": [ 207 | "%timeit pairwise_python(X)" 208 | ], 209 | "execution_count": null, 210 | "outputs": [ 211 | { 212 | "output_type": "stream", 213 | "text": [ 214 | "1 loop, best of 5: 5.77 s per loop\n" 215 | ], 216 | "name": "stdout" 217 | } 218 | ] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": { 223 | "id": "OZiRy9IwOppu" 224 | }, 225 | "source": [ 226 | "## Numpy Function With Broadcasting" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": { 232 | "id": "vtOPcGszOppu" 233 | }, 234 | "source": [ 235 | "Numpy broadcasting is an abstraction that allows loops over array indices to be\n", 236 | "executed in compiled C. \n", 237 | "\n" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "metadata": { 243 | "colab": { 244 | "base_uri": "https://localhost:8080/" 245 | }, 246 | "id": "qxzX1AOxOppu", 247 | "outputId": "4a4715b4-cce1-4e3f-a8ac-af8aa140562e" 248 | }, 249 | "source": [ 250 | "def pairwise_numpy(X):\n", 251 | " return np.sqrt(((X[:, None, :] - X) ** 2).sum(-1))\n", 252 | "%timeit pairwise_numpy(X)" 253 | ], 254 | "execution_count": null, 255 | "outputs": [ 256 | { 257 | "output_type": "stream", 258 | "text": [ 259 | "10 loops, best of 5: 39.4 ms per loop\n" 260 | ], 261 | "name": "stdout" 262 | } 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": { 268 | "id": "8CvCyuwkh46U" 269 | }, 270 | "source": [ 271 | "It should be noted that when broadcasting, numpy ends up allocating hidden temporary arrays which can eat up memory and cause computational overhead." 272 | ] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "metadata": { 277 | "id": "XDtSq-ydOQz-" 278 | }, 279 | "source": [ 280 | "## pypy" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "metadata": { 286 | "colab": { 287 | "base_uri": "https://localhost:8080/" 288 | }, 289 | "id": "hI5dVrnrOTvP", 290 | "outputId": "fd53d68b-983d-43fe-8a74-011f83c68649" 291 | }, 292 | "source": [ 293 | "!apt-get install pypy" 294 | ], 295 | "execution_count": null, 296 | "outputs": [ 297 | { 298 | "output_type": "stream", 299 | "text": [ 300 | "Reading package lists... Done\n", 301 | "Building dependency tree \n", 302 | "Reading state information... Done\n", 303 | "The following additional packages will be installed:\n", 304 | " pypy-lib\n", 305 | "Suggested packages:\n", 306 | " pypy-doc pypy-tk\n", 307 | "The following NEW packages will be installed:\n", 308 | " pypy pypy-lib\n", 309 | "0 upgraded, 2 newly installed, 0 to remove and 30 not upgraded.\n", 310 | "Need to get 13.1 MB of archives.\n", 311 | "After this operation, 84.6 MB of additional disk space will be used.\n", 312 | "Get:1 http://archive.ubuntu.com/ubuntu bionic/universe amd64 pypy-lib amd64 5.10.0+dfsg-3build2 [2,303 kB]\n", 313 | "Get:2 http://archive.ubuntu.com/ubuntu bionic/universe amd64 pypy amd64 5.10.0+dfsg-3build2 [10.8 MB]\n", 314 | "Fetched 13.1 MB in 1s (11.2 MB/s)\n", 315 | "Selecting previously unselected package pypy-lib:amd64.\n", 316 | "(Reading database ... 160980 files and directories currently installed.)\n", 317 | "Preparing to unpack .../pypy-lib_5.10.0+dfsg-3build2_amd64.deb ...\n", 318 | "Unpacking pypy-lib:amd64 (5.10.0+dfsg-3build2) ...\n", 319 | "Selecting previously unselected package pypy.\n", 320 | "Preparing to unpack .../pypy_5.10.0+dfsg-3build2_amd64.deb ...\n", 321 | "Unpacking pypy (5.10.0+dfsg-3build2) ...\n", 322 | "Setting up pypy-lib:amd64 (5.10.0+dfsg-3build2) ...\n", 323 | "Setting up pypy (5.10.0+dfsg-3build2) ...\n", 324 | "running pypy rtupdate hooks for 5.10\n", 325 | "running pypy post-rtupdate hooks for 5.10\n", 326 | "Processing triggers for man-db (2.8.3-2ubuntu0.1) ...\n" 327 | ], 328 | "name": "stdout" 329 | } 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "metadata": { 335 | "colab": { 336 | "base_uri": "https://localhost:8080/" 337 | }, 338 | "id": "cmIzCs8dTJQJ", 339 | "outputId": "34ee99d7-faaf-403a-a5ca-2382b0873515" 340 | }, 341 | "source": [ 342 | "%%pypy\n", 343 | "import math\n", 344 | "print(math.sqrt(4.0))\n", 345 | "rows, cols = (5, 5)\n", 346 | "arr=[]\n", 347 | "for i in range(rows):\n", 348 | " col = []\n", 349 | " for j in range(cols):\n", 350 | " col.append(0)\n", 351 | " arr.append(col)\n", 352 | "print(arr)" 353 | ], 354 | "execution_count": null, 355 | "outputs": [ 356 | { 357 | "output_type": "stream", 358 | "text": [ 359 | "2.0\n", 360 | "[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]\n" 361 | ], 362 | "name": "stdout" 363 | } 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "metadata": { 369 | "colab": { 370 | "base_uri": "https://localhost:8080/" 371 | }, 372 | "id": "eD9eqVMQOdFI", 373 | "outputId": "f6d03f93-9208-491c-a188-cc53a50555ee" 374 | }, 375 | "source": [ 376 | "%%timeit \n", 377 | "%%pypy\n", 378 | "from random import random\n", 379 | "import math\n", 380 | "M = 1000\n", 381 | "N = 3\n", 382 | "D=[]\n", 383 | "def pairwise_pypy(M, N):\n", 384 | " #D = np.empty((M, M), dtype=np.float)\n", 385 | " for i in range(M):\n", 386 | " for j in range(M):\n", 387 | " d = 0.0\n", 388 | " col = []\n", 389 | " for k in range(N):\n", 390 | " tmp = random() - random()\n", 391 | " d += tmp * tmp\n", 392 | " col.append( math.sqrt(d) )\n", 393 | " D.append(col)\n", 394 | " return D\n", 395 | "pairwise_pypy(M, N)" 396 | ], 397 | "execution_count": null, 398 | "outputs": [ 399 | { 400 | "output_type": "stream", 401 | "text": [ 402 | "1 loop, best of 5: 191 ms per loop\n" 403 | ], 404 | "name": "stdout" 405 | } 406 | ] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "metadata": { 411 | "id": "EIDAjo57Oppx" 412 | }, 413 | "source": [ 414 | "## Numba" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "metadata": { 420 | "colab": { 421 | "base_uri": "https://localhost:8080/" 422 | }, 423 | "id": "iWqiuqNOOppy", 424 | "outputId": "30bebe2d-beda-4ac7-ae83-ce02da848976" 425 | }, 426 | "source": [ 427 | "from numba import double\n", 428 | "#from numba.decorators import jit, autojit\n", 429 | "from numba import jit\n", 430 | "\n", 431 | "pairwise_numba = jit(pairwise_python)\n", 432 | "\n", 433 | "%timeit pairwise_numba(X)" 434 | ], 435 | "execution_count": null, 436 | "outputs": [ 437 | { 438 | "output_type": "stream", 439 | "text": [ 440 | ":1: NumbaWarning: \n", 441 | "Compilation is falling back to object mode WITH looplifting enabled because Function \"pairwise_python\" failed type inference due to: No implementation of function Function() found for signature:\n", 442 | " \n", 443 | " >>> empty(UniTuple(int64 x 2), dtype=Function())\n", 444 | " \n", 445 | "There are 2 candidate implementations:\n", 446 | " - Of which 2 did not match due to:\n", 447 | " Overload of function 'empty': File: numba/core/typing/npydecl.py: Line 504.\n", 448 | " With argument(s): '(UniTuple(int64 x 2), dtype=Function())':\n", 449 | " No match.\n", 450 | "\n", 451 | "During: resolving callee type: Function()\n", 452 | "During: typing of call at (4)\n", 453 | "\n", 454 | "\n", 455 | "File \"\", line 4:\n", 456 | "def pairwise_python(X):\n", 457 | " \n", 458 | " N = X.shape[1]\n", 459 | " D = np.empty((M, M), dtype=np.float)\n", 460 | " ^\n", 461 | "\n", 462 | " def pairwise_python(X):\n", 463 | ":1: NumbaWarning: \n", 464 | "Compilation is falling back to object mode WITHOUT looplifting enabled because Function \"pairwise_python\" failed type inference due to: cannot determine Numba type of \n", 465 | "\n", 466 | "File \"\", line 5:\n", 467 | "def pairwise_python(X):\n", 468 | " \n", 469 | " D = np.empty((M, M), dtype=np.float)\n", 470 | " for i in range(M):\n", 471 | " ^\n", 472 | "\n", 473 | " def pairwise_python(X):\n", 474 | "/usr/local/lib/python3.7/dist-packages/numba/core/object_mode_passes.py:178: NumbaWarning: Function \"pairwise_python\" was compiled in object mode without forceobj=True, but has lifted loops.\n", 475 | "\n", 476 | "File \"\", line 2:\n", 477 | "def pairwise_python(X):\n", 478 | " M = X.shape[0]\n", 479 | " ^\n", 480 | "\n", 481 | " state.func_ir.loc))\n", 482 | "/usr/local/lib/python3.7/dist-packages/numba/core/object_mode_passes.py:188: NumbaDeprecationWarning: \n", 483 | "Fall-back from the nopython compilation path to the object mode compilation path has been detected, this is deprecated behaviour.\n", 484 | "\n", 485 | "For more information visit https://numba.pydata.org/numba-doc/latest/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit\n", 486 | "\n", 487 | "File \"\", line 2:\n", 488 | "def pairwise_python(X):\n", 489 | " M = X.shape[0]\n", 490 | " ^\n", 491 | "\n", 492 | " state.func_ir.loc))\n" 493 | ], 494 | "name": "stderr" 495 | }, 496 | { 497 | "output_type": "stream", 498 | "text": [ 499 | "The slowest run took 159.75 times longer than the fastest. This could mean that an intermediate result is being cached.\n", 500 | "1 loop, best of 5: 3.1 ms per loop\n" 501 | ], 502 | "name": "stdout" 503 | } 504 | ] 505 | }, 506 | { 507 | "cell_type": "markdown", 508 | "metadata": { 509 | "id": "Pvor_AOzacqJ" 510 | }, 511 | "source": [ 512 | "## numexpr" 513 | ] 514 | }, 515 | { 516 | "cell_type": "code", 517 | "metadata": { 518 | "colab": { 519 | "base_uri": "https://localhost:8080/" 520 | }, 521 | "id": "JHJUPUnWae2w", 522 | "outputId": "a5b5002f-3f5a-456d-9e6f-cfabaab3c630" 523 | }, 524 | "source": [ 525 | "import numpy as np\n", 526 | "import numexpr as ne\n", 527 | "def pairwise_numexpr(X):\n", 528 | " #return np.sqrt(((X[:, None, :] - X) ** 2).sum(-1))\n", 529 | " Y = (X[:, None, :] - X).view(dtype=np.float)\n", 530 | " S = ne.evaluate(\"sum(Y**2, axis=2)\")\n", 531 | " return ne.evaluate(\"sqrt(S)\")\n", 532 | "%timeit pairwise_numexpr(X)" 533 | ], 534 | "execution_count": null, 535 | "outputs": [ 536 | { 537 | "output_type": "stream", 538 | "text": [ 539 | "10 loops, best of 5: 20.8 ms per loop\n" 540 | ], 541 | "name": "stdout" 542 | } 543 | ] 544 | }, 545 | { 546 | "cell_type": "markdown", 547 | "metadata": { 548 | "id": "jWK_iD0kPhju" 549 | }, 550 | "source": [ 551 | "## Pythran" 552 | ] 553 | }, 554 | { 555 | "cell_type": "code", 556 | "metadata": { 557 | "colab": { 558 | "base_uri": "https://localhost:8080/" 559 | }, 560 | "id": "nhUdwEd7Pdf-", 561 | "outputId": "b1435cdb-c64e-42bc-ce81-910e992c4cad" 562 | }, 563 | "source": [ 564 | "!apt-get install libatlas-base-dev\n", 565 | "!apt-get install python-dev python-ply python-networkx python-numpy\n", 566 | "!pip install pythran" 567 | ], 568 | "execution_count": null, 569 | "outputs": [ 570 | { 571 | "output_type": "stream", 572 | "text": [ 573 | "Reading package lists... Done\n", 574 | "Building dependency tree \n", 575 | "Reading state information... Done\n", 576 | "libatlas-base-dev is already the newest version (3.10.3-5).\n", 577 | "0 upgraded, 0 newly installed, 0 to remove and 30 not upgraded.\n", 578 | "Reading package lists... Done\n", 579 | "Building dependency tree \n", 580 | "Reading state information... Done\n", 581 | "python-dev is already the newest version (2.7.15~rc1-1).\n", 582 | "python-numpy is already the newest version (1:1.13.3-2ubuntu1).\n", 583 | "python-numpy set to manually installed.\n", 584 | "The following additional packages will be installed:\n", 585 | " python-decorator python-pkg-resources python-yaml\n", 586 | "Suggested packages:\n", 587 | " python-matplotlib python-pydotplus python-scipy python-pygraphviz\n", 588 | " | python-pydot python-setuptools python-ply-doc\n", 589 | "The following NEW packages will be installed:\n", 590 | " python-decorator python-networkx python-pkg-resources python-ply python-yaml\n", 591 | "0 upgraded, 5 newly installed, 0 to remove and 30 not upgraded.\n", 592 | "Need to get 1,103 kB of archives.\n", 593 | "After this operation, 5,348 kB of additional disk space will be used.\n", 594 | "Get:1 http://archive.ubuntu.com/ubuntu bionic/main amd64 python-decorator all 4.1.2-1 [9,300 B]\n", 595 | "Get:2 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 python-networkx all 1.11-1ubuntu3 [804 kB]\n", 596 | "Get:3 http://archive.ubuntu.com/ubuntu bionic/main amd64 python-pkg-resources all 39.0.1-2 [128 kB]\n", 597 | "Get:4 http://archive.ubuntu.com/ubuntu bionic/main amd64 python-ply all 3.11-1 [46.6 kB]\n", 598 | "Get:5 http://archive.ubuntu.com/ubuntu bionic/main amd64 python-yaml amd64 3.12-1build2 [115 kB]\n", 599 | "Fetched 1,103 kB in 1s (867 kB/s)\n", 600 | "Selecting previously unselected package python-decorator.\n", 601 | "(Reading database ... 160980 files and directories currently installed.)\n", 602 | "Preparing to unpack .../python-decorator_4.1.2-1_all.deb ...\n", 603 | "Unpacking python-decorator (4.1.2-1) ...\n", 604 | "Selecting previously unselected package python-networkx.\n", 605 | "Preparing to unpack .../python-networkx_1.11-1ubuntu3_all.deb ...\n", 606 | "Unpacking python-networkx (1.11-1ubuntu3) ...\n", 607 | "Selecting previously unselected package python-pkg-resources.\n", 608 | "Preparing to unpack .../python-pkg-resources_39.0.1-2_all.deb ...\n", 609 | "Unpacking python-pkg-resources (39.0.1-2) ...\n", 610 | "Selecting previously unselected package python-ply.\n", 611 | "Preparing to unpack .../python-ply_3.11-1_all.deb ...\n", 612 | "Unpacking python-ply (3.11-1) ...\n", 613 | "Selecting previously unselected package python-yaml.\n", 614 | "Preparing to unpack .../python-yaml_3.12-1build2_amd64.deb ...\n", 615 | "Unpacking python-yaml (3.12-1build2) ...\n", 616 | "Setting up python-yaml (3.12-1build2) ...\n", 617 | "Setting up python-ply (3.11-1) ...\n", 618 | "Setting up python-pkg-resources (39.0.1-2) ...\n", 619 | "Setting up python-decorator (4.1.2-1) ...\n", 620 | "Setting up python-networkx (1.11-1ubuntu3) ...\n", 621 | "Processing triggers for man-db (2.8.3-2ubuntu0.1) ...\n", 622 | "Collecting pythran\n", 623 | "\u001b[?25l Downloading https://files.pythonhosted.org/packages/3e/f1/01c3378136d456ab890356132bbc17f0fb83d881162aa87366836b679b58/pythran-0.9.8.post2-py3-none-any.whl (4.4MB)\n", 624 | "\u001b[K |████████████████████████████████| 4.4MB 8.1MB/s \n", 625 | "\u001b[?25hRequirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from pythran) (1.15.0)\n", 626 | "Collecting gast~=0.4.0\n", 627 | " Downloading https://files.pythonhosted.org/packages/b6/48/583c032b79ae5b3daa02225a675aeb673e58d2cb698e78510feceb11958c/gast-0.4.0-py3-none-any.whl\n", 628 | "Requirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from pythran) (1.19.5)\n", 629 | "Collecting beniget~=0.3.0\n", 630 | " Downloading https://files.pythonhosted.org/packages/af/14/210eb0053124922abbd1ace0fe6fe46782acda64edb101f5fc890a72f6ad/beniget-0.3.0-py3-none-any.whl\n", 631 | "Collecting ply>=3.4\n", 632 | "\u001b[?25l Downloading https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl (49kB)\n", 633 | "\u001b[K |████████████████████████████████| 51kB 6.4MB/s \n", 634 | "\u001b[?25hRequirement already satisfied: networkx>=2 in /usr/local/lib/python3.7/dist-packages (from pythran) (2.5)\n", 635 | "Requirement already satisfied: decorator in /usr/local/lib/python3.7/dist-packages (from pythran) (4.4.2)\n", 636 | "\u001b[31mERROR: tensorflow 2.4.1 has requirement gast==0.3.3, but you'll have gast 0.4.0 which is incompatible.\u001b[0m\n", 637 | "Installing collected packages: gast, beniget, ply, pythran\n", 638 | " Found existing installation: gast 0.3.3\n", 639 | " Uninstalling gast-0.3.3:\n", 640 | " Successfully uninstalled gast-0.3.3\n", 641 | "Successfully installed beniget-0.3.0 gast-0.4.0 ply-3.11 pythran-0.9.8.post2\n" 642 | ], 643 | "name": "stdout" 644 | } 645 | ] 646 | }, 647 | { 648 | "cell_type": "code", 649 | "metadata": { 650 | "id": "KYUTbqc6PqfL" 651 | }, 652 | "source": [ 653 | "import pythran\n", 654 | "%load_ext pythran.magic" 655 | ], 656 | "execution_count": null, 657 | "outputs": [] 658 | }, 659 | { 660 | "cell_type": "code", 661 | "metadata": { 662 | "id": "ZZReoD4JP4B5" 663 | }, 664 | "source": [ 665 | "%%pythran\n", 666 | "#pythran export pairwise_pythran(float[][])\n", 667 | "import numpy as np\n", 668 | "def pairwise_pythran(X):\n", 669 | " M = X.shape[0]\n", 670 | " N = X.shape[1]\n", 671 | " D = np.empty((M, M), dtype=np.float)\n", 672 | " for i in range(M):\n", 673 | " for j in range(M):\n", 674 | " d = 0.0\n", 675 | " for k in range(N):\n", 676 | " tmp = X[i, k] - X[j, k]\n", 677 | " d += tmp * tmp\n", 678 | " D[i, j] = np.sqrt(d)\n", 679 | " return D" 680 | ], 681 | "execution_count": null, 682 | "outputs": [] 683 | }, 684 | { 685 | "cell_type": "code", 686 | "metadata": { 687 | "colab": { 688 | "base_uri": "https://localhost:8080/" 689 | }, 690 | "id": "NXgogxUAX1C5", 691 | "outputId": "466761c9-d8f0-4b51-dece-c297a584d0e5" 692 | }, 693 | "source": [ 694 | "%timeit pairwise_pythran(X)" 695 | ], 696 | "execution_count": null, 697 | "outputs": [ 698 | { 699 | "output_type": "stream", 700 | "text": [ 701 | "100 loops, best of 5: 3.79 ms per loop\n" 702 | ], 703 | "name": "stdout" 704 | } 705 | ] 706 | }, 707 | { 708 | "cell_type": "markdown", 709 | "metadata": { 710 | "id": "_uarG_4SOppy" 711 | }, 712 | "source": [ 713 | "## Cython" 714 | ] 715 | }, 716 | { 717 | "cell_type": "code", 718 | "metadata": { 719 | "id": "WVgF0ldlOppz" 720 | }, 721 | "source": [ 722 | "#%load_ext cythonmagic\n", 723 | "%load_ext Cython" 724 | ], 725 | "execution_count": null, 726 | "outputs": [] 727 | }, 728 | { 729 | "cell_type": "code", 730 | "metadata": { 731 | "colab": { 732 | "base_uri": "https://localhost:8080/", 733 | "height": 426 734 | }, 735 | "id": "9AvseFBFOppz", 736 | "outputId": "cbd2463d-3146-4b27-887d-c15ed632ed69" 737 | }, 738 | "source": [ 739 | "%%cython -a\n", 740 | "\n", 741 | "import numpy as np\n", 742 | "cimport cython\n", 743 | "from libc.math cimport sqrt\n", 744 | "\n", 745 | "@cython.boundscheck(False)\n", 746 | "@cython.wraparound(False)\n", 747 | "def pairwise_cython(double[:, ::1] X):\n", 748 | " cdef int M = X.shape[0]\n", 749 | " cdef int N = X.shape[1]\n", 750 | " cdef double tmp, d\n", 751 | " cdef double[:, ::1] D = np.empty((M, M), dtype=np.float64)\n", 752 | " for i in range(M):\n", 753 | " for j in range(M):\n", 754 | " d = 0.0\n", 755 | " for k in range(N):\n", 756 | " tmp = X[i, k] - X[j, k]\n", 757 | " d += tmp * tmp\n", 758 | " D[i, j] = sqrt(d)\n", 759 | " return np.asarray(D)" 760 | ], 761 | "execution_count": null, 762 | "outputs": [ 763 | { 764 | "output_type": "execute_result", 765 | "data": { 766 | "text/html": [ 767 | "\n", 768 | "\n", 769 | "\n", 770 | "\n", 771 | " \n", 772 | " Cython: _cython_magic_b37f9bae97d71ca0cd15be30d08e2217.pyx\n", 773 | " \n", 1125 | "\n", 1126 | "\n", 1127 | "

Generated by Cython 0.29.22

\n", 1128 | "

\n", 1129 | " Yellow lines hint at Python interaction.
\n", 1130 | " Click on a line that starts with a \"+\" to see the C code that Cython generated for it.\n", 1131 | "

\n", 1132 | "
 01: 
\n", 1133 | "
+02: import numpy as np
\n", 1134 | "
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_numpy, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error)\n",
1135 |               "  __Pyx_GOTREF(__pyx_t_1);\n",
1136 |               "  if (PyDict_SetItem(__pyx_d, __pyx_n_s_np, __pyx_t_1) < 0) __PYX_ERR(0, 2, __pyx_L1_error)\n",
1137 |               "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
1138 |               "/* … */\n",
1139 |               "  __pyx_t_1 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error)\n",
1140 |               "  __Pyx_GOTREF(__pyx_t_1);\n",
1141 |               "  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(0, 2, __pyx_L1_error)\n",
1142 |               "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
1143 |               "
 03: cimport cython
\n", 1144 | "
 04: from libc.math cimport sqrt
\n", 1145 | "
 05: 
\n", 1146 | "
 06: @cython.boundscheck(False)
\n", 1147 | "
 07: @cython.wraparound(False)
\n", 1148 | "
+08: def pairwise_cython(double[:, ::1] X):
\n", 1149 | "
/* Python wrapper */\n",
1150 |               "static PyObject *__pyx_pw_46_cython_magic_b37f9bae97d71ca0cd15be30d08e2217_1pairwise_cython(PyObject *__pyx_self, PyObject *__pyx_arg_X); /*proto*/\n",
1151 |               "static PyMethodDef __pyx_mdef_46_cython_magic_b37f9bae97d71ca0cd15be30d08e2217_1pairwise_cython = {\"pairwise_cython\", (PyCFunction)__pyx_pw_46_cython_magic_b37f9bae97d71ca0cd15be30d08e2217_1pairwise_cython, METH_O, 0};\n",
1152 |               "static PyObject *__pyx_pw_46_cython_magic_b37f9bae97d71ca0cd15be30d08e2217_1pairwise_cython(PyObject *__pyx_self, PyObject *__pyx_arg_X) {\n",
1153 |               "  __Pyx_memviewslice __pyx_v_X = { 0, 0, { 0 }, { 0 }, { 0 } };\n",
1154 |               "  PyObject *__pyx_r = 0;\n",
1155 |               "  __Pyx_RefNannyDeclarations\n",
1156 |               "  __Pyx_RefNannySetupContext(\"pairwise_cython (wrapper)\", 0);\n",
1157 |               "  assert(__pyx_arg_X); {\n",
1158 |               "    __pyx_v_X = __Pyx_PyObject_to_MemoryviewSlice_d_dc_double(__pyx_arg_X, PyBUF_WRITABLE); if (unlikely(!__pyx_v_X.memview)) __PYX_ERR(0, 8, __pyx_L3_error)\n",
1159 |               "  }\n",
1160 |               "  goto __pyx_L4_argument_unpacking_done;\n",
1161 |               "  __pyx_L3_error:;\n",
1162 |               "  __Pyx_AddTraceback(\"_cython_magic_b37f9bae97d71ca0cd15be30d08e2217.pairwise_cython\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
1163 |               "  __Pyx_RefNannyFinishContext();\n",
1164 |               "  return NULL;\n",
1165 |               "  __pyx_L4_argument_unpacking_done:;\n",
1166 |               "  __pyx_r = __pyx_pf_46_cython_magic_b37f9bae97d71ca0cd15be30d08e2217_pairwise_cython(__pyx_self, __pyx_v_X);\n",
1167 |               "  int __pyx_lineno = 0;\n",
1168 |               "  const char *__pyx_filename = NULL;\n",
1169 |               "  int __pyx_clineno = 0;\n",
1170 |               "\n",
1171 |               "  /* function exit code */\n",
1172 |               "  __Pyx_RefNannyFinishContext();\n",
1173 |               "  return __pyx_r;\n",
1174 |               "}\n",
1175 |               "\n",
1176 |               "static PyObject *__pyx_pf_46_cython_magic_b37f9bae97d71ca0cd15be30d08e2217_pairwise_cython(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_X) {\n",
1177 |               "  int __pyx_v_M;\n",
1178 |               "  int __pyx_v_N;\n",
1179 |               "  double __pyx_v_tmp;\n",
1180 |               "  double __pyx_v_d;\n",
1181 |               "  __Pyx_memviewslice __pyx_v_D = { 0, 0, { 0 }, { 0 }, { 0 } };\n",
1182 |               "  int __pyx_v_i;\n",
1183 |               "  int __pyx_v_j;\n",
1184 |               "  int __pyx_v_k;\n",
1185 |               "  PyObject *__pyx_r = NULL;\n",
1186 |               "  __Pyx_RefNannyDeclarations\n",
1187 |               "  __Pyx_RefNannySetupContext(\"pairwise_cython\", 0);\n",
1188 |               "/* … */\n",
1189 |               "  /* function exit code */\n",
1190 |               "  __pyx_L1_error:;\n",
1191 |               "  __Pyx_XDECREF(__pyx_t_1);\n",
1192 |               "  __Pyx_XDECREF(__pyx_t_2);\n",
1193 |               "  __Pyx_XDECREF(__pyx_t_3);\n",
1194 |               "  __Pyx_XDECREF(__pyx_t_4);\n",
1195 |               "  __Pyx_XDECREF(__pyx_t_5);\n",
1196 |               "  __PYX_XDEC_MEMVIEW(&__pyx_t_6, 1);\n",
1197 |               "  __Pyx_AddTraceback(\"_cython_magic_b37f9bae97d71ca0cd15be30d08e2217.pairwise_cython\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
1198 |               "  __pyx_r = NULL;\n",
1199 |               "  __pyx_L0:;\n",
1200 |               "  __PYX_XDEC_MEMVIEW(&__pyx_v_X, 1);\n",
1201 |               "  __PYX_XDEC_MEMVIEW(&__pyx_v_D, 1);\n",
1202 |               "  __Pyx_XGIVEREF(__pyx_r);\n",
1203 |               "  __Pyx_RefNannyFinishContext();\n",
1204 |               "  return __pyx_r;\n",
1205 |               "}\n",
1206 |               "/* … */\n",
1207 |               "  __pyx_tuple__19 = PyTuple_Pack(10, __pyx_n_s_X, __pyx_n_s_X, __pyx_n_s_M, __pyx_n_s_N, __pyx_n_s_tmp, __pyx_n_s_d, __pyx_n_s_D, __pyx_n_s_i, __pyx_n_s_j, __pyx_n_s_k); if (unlikely(!__pyx_tuple__19)) __PYX_ERR(0, 8, __pyx_L1_error)\n",
1208 |               "  __Pyx_GOTREF(__pyx_tuple__19);\n",
1209 |               "  __Pyx_GIVEREF(__pyx_tuple__19);\n",
1210 |               "/* … */\n",
1211 |               "  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_b37f9bae97d71ca0cd15be30d08e2217_1pairwise_cython, NULL, __pyx_n_s_cython_magic_b37f9bae97d71ca0cd); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 8, __pyx_L1_error)\n",
1212 |               "  __Pyx_GOTREF(__pyx_t_1);\n",
1213 |               "  if (PyDict_SetItem(__pyx_d, __pyx_n_s_pairwise_cython, __pyx_t_1) < 0) __PYX_ERR(0, 8, __pyx_L1_error)\n",
1214 |               "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
1215 |               "  __pyx_codeobj__20 = (PyObject*)__Pyx_PyCode_New(1, 0, 10, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__19, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_root_cache_ipython_cython__cyth, __pyx_n_s_pairwise_cython, 8, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__20)) __PYX_ERR(0, 8, __pyx_L1_error)\n",
1216 |               "
+09:     cdef int M = X.shape[0]
\n", 1217 | "
  __pyx_v_M = (__pyx_v_X.shape[0]);\n",
1218 |               "
+10:     cdef int N = X.shape[1]
\n", 1219 | "
  __pyx_v_N = (__pyx_v_X.shape[1]);\n",
1220 |               "
 11:     cdef double tmp, d
\n", 1221 | "
+12:     cdef double[:, ::1] D = np.empty((M, M), dtype=np.float64)
\n", 1222 | "
  __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_n_s_np); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1223 |               "  __Pyx_GOTREF(__pyx_t_1);\n",
1224 |               "  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_empty); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1225 |               "  __Pyx_GOTREF(__pyx_t_2);\n",
1226 |               "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
1227 |               "  __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_M); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1228 |               "  __Pyx_GOTREF(__pyx_t_1);\n",
1229 |               "  __pyx_t_3 = __Pyx_PyInt_From_int(__pyx_v_M); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1230 |               "  __Pyx_GOTREF(__pyx_t_3);\n",
1231 |               "  __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1232 |               "  __Pyx_GOTREF(__pyx_t_4);\n",
1233 |               "  __Pyx_GIVEREF(__pyx_t_1);\n",
1234 |               "  PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_1);\n",
1235 |               "  __Pyx_GIVEREF(__pyx_t_3);\n",
1236 |               "  PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_t_3);\n",
1237 |               "  __pyx_t_1 = 0;\n",
1238 |               "  __pyx_t_3 = 0;\n",
1239 |               "  __pyx_t_3 = PyTuple_New(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1240 |               "  __Pyx_GOTREF(__pyx_t_3);\n",
1241 |               "  __Pyx_GIVEREF(__pyx_t_4);\n",
1242 |               "  PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_4);\n",
1243 |               "  __pyx_t_4 = 0;\n",
1244 |               "  __pyx_t_4 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1245 |               "  __Pyx_GOTREF(__pyx_t_4);\n",
1246 |               "  __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_n_s_np); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1247 |               "  __Pyx_GOTREF(__pyx_t_1);\n",
1248 |               "  __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_float64); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1249 |               "  __Pyx_GOTREF(__pyx_t_5);\n",
1250 |               "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
1251 |               "  if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_dtype, __pyx_t_5) < 0) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1252 |               "  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;\n",
1253 |               "  __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_t_3, __pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1254 |               "  __Pyx_GOTREF(__pyx_t_5);\n",
1255 |               "  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
1256 |               "  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;\n",
1257 |               "  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;\n",
1258 |               "  __pyx_t_6 = __Pyx_PyObject_to_MemoryviewSlice_d_dc_double(__pyx_t_5, PyBUF_WRITABLE); if (unlikely(!__pyx_t_6.memview)) __PYX_ERR(0, 12, __pyx_L1_error)\n",
1259 |               "  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;\n",
1260 |               "  __pyx_v_D = __pyx_t_6;\n",
1261 |               "  __pyx_t_6.memview = NULL;\n",
1262 |               "  __pyx_t_6.data = NULL;\n",
1263 |               "
+13:     for i in range(M):
\n", 1264 | "
  __pyx_t_7 = __pyx_v_M;\n",
1265 |               "  __pyx_t_8 = __pyx_t_7;\n",
1266 |               "  for (__pyx_t_9 = 0; __pyx_t_9 < __pyx_t_8; __pyx_t_9+=1) {\n",
1267 |               "    __pyx_v_i = __pyx_t_9;\n",
1268 |               "
+14:         for j in range(M):
\n", 1269 | "
    __pyx_t_10 = __pyx_v_M;\n",
1270 |               "    __pyx_t_11 = __pyx_t_10;\n",
1271 |               "    for (__pyx_t_12 = 0; __pyx_t_12 < __pyx_t_11; __pyx_t_12+=1) {\n",
1272 |               "      __pyx_v_j = __pyx_t_12;\n",
1273 |               "
+15:             d = 0.0
\n", 1274 | "
      __pyx_v_d = 0.0;\n",
1275 |               "
+16:             for k in range(N):
\n", 1276 | "
      __pyx_t_13 = __pyx_v_N;\n",
1277 |               "      __pyx_t_14 = __pyx_t_13;\n",
1278 |               "      for (__pyx_t_15 = 0; __pyx_t_15 < __pyx_t_14; __pyx_t_15+=1) {\n",
1279 |               "        __pyx_v_k = __pyx_t_15;\n",
1280 |               "
+17:                 tmp = X[i, k] - X[j, k]
\n", 1281 | "
        __pyx_t_16 = __pyx_v_i;\n",
1282 |               "        __pyx_t_17 = __pyx_v_k;\n",
1283 |               "        __pyx_t_18 = __pyx_v_j;\n",
1284 |               "        __pyx_t_19 = __pyx_v_k;\n",
1285 |               "        __pyx_v_tmp = ((*((double *) ( /* dim=1 */ ((char *) (((double *) ( /* dim=0 */ (__pyx_v_X.data + __pyx_t_16 * __pyx_v_X.strides[0]) )) + __pyx_t_17)) ))) - (*((double *) ( /* dim=1 */ ((char *) (((double *) ( /* dim=0 */ (__pyx_v_X.data + __pyx_t_18 * __pyx_v_X.strides[0]) )) + __pyx_t_19)) ))));\n",
1286 |               "
+18:                 d += tmp * tmp
\n", 1287 | "
        __pyx_v_d = (__pyx_v_d + (__pyx_v_tmp * __pyx_v_tmp));\n",
1288 |               "      }\n",
1289 |               "
+19:             D[i, j] = sqrt(d)
\n", 1290 | "
      __pyx_t_19 = __pyx_v_i;\n",
1291 |               "      __pyx_t_18 = __pyx_v_j;\n",
1292 |               "      *((double *) ( /* dim=1 */ ((char *) (((double *) ( /* dim=0 */ (__pyx_v_D.data + __pyx_t_19 * __pyx_v_D.strides[0]) )) + __pyx_t_18)) )) = sqrt(__pyx_v_d);\n",
1293 |               "    }\n",
1294 |               "  }\n",
1295 |               "
+20:     return np.asarray(D)
\n", 1296 | "
  __Pyx_XDECREF(__pyx_r);\n",
1297 |               "  __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_n_s_np); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 20, __pyx_L1_error)\n",
1298 |               "  __Pyx_GOTREF(__pyx_t_4);\n",
1299 |               "  __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s_asarray); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 20, __pyx_L1_error)\n",
1300 |               "  __Pyx_GOTREF(__pyx_t_3);\n",
1301 |               "  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;\n",
1302 |               "  __pyx_t_4 = __pyx_memoryview_fromslice(__pyx_v_D, 2, (PyObject *(*)(char *)) __pyx_memview_get_double, (int (*)(char *, PyObject *)) __pyx_memview_set_double, 0);; if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 20, __pyx_L1_error)\n",
1303 |               "  __Pyx_GOTREF(__pyx_t_4);\n",
1304 |               "  __pyx_t_2 = NULL;\n",
1305 |               "  if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_3))) {\n",
1306 |               "    __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);\n",
1307 |               "    if (likely(__pyx_t_2)) {\n",
1308 |               "      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_3);\n",
1309 |               "      __Pyx_INCREF(__pyx_t_2);\n",
1310 |               "      __Pyx_INCREF(function);\n",
1311 |               "      __Pyx_DECREF_SET(__pyx_t_3, function);\n",
1312 |               "    }\n",
1313 |               "  }\n",
1314 |               "  __pyx_t_5 = (__pyx_t_2) ? __Pyx_PyObject_Call2Args(__pyx_t_3, __pyx_t_2, __pyx_t_4) : __Pyx_PyObject_CallOneArg(__pyx_t_3, __pyx_t_4);\n",
1315 |               "  __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;\n",
1316 |               "  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;\n",
1317 |               "  if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 20, __pyx_L1_error)\n",
1318 |               "  __Pyx_GOTREF(__pyx_t_5);\n",
1319 |               "  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;\n",
1320 |               "  __pyx_r = __pyx_t_5;\n",
1321 |               "  __pyx_t_5 = 0;\n",
1322 |               "  goto __pyx_L0;\n",
1323 |               "
" 1324 | ], 1325 | "text/plain": [ 1326 | "" 1327 | ] 1328 | }, 1329 | "metadata": { 1330 | "tags": [] 1331 | }, 1332 | "execution_count": 12 1333 | } 1334 | ] 1335 | }, 1336 | { 1337 | "cell_type": "code", 1338 | "metadata": { 1339 | "colab": { 1340 | "base_uri": "https://localhost:8080/" 1341 | }, 1342 | "id": "ART1zqv3Opp0", 1343 | "outputId": "63952e43-357b-4f1c-ae53-99d78a25c9a8" 1344 | }, 1345 | "source": [ 1346 | "%timeit pairwise_cython(X)" 1347 | ], 1348 | "execution_count": null, 1349 | "outputs": [ 1350 | { 1351 | "output_type": "stream", 1352 | "text": [ 1353 | "100 loops, best of 5: 4.04 ms per loop\n" 1354 | ], 1355 | "name": "stdout" 1356 | } 1357 | ] 1358 | }, 1359 | { 1360 | "cell_type": "markdown", 1361 | "metadata": { 1362 | "id": "Wt6nR4iaOpp1" 1363 | }, 1364 | "source": [ 1365 | "## Fortran/F2Py" 1366 | ] 1367 | }, 1368 | { 1369 | "cell_type": "code", 1370 | "metadata": { 1371 | "colab": { 1372 | "base_uri": "https://localhost:8080/" 1373 | }, 1374 | "id": "LkASbSTxOpp1", 1375 | "outputId": "e1fcce3a-f5f4-4ef7-c931-584821a6f273" 1376 | }, 1377 | "source": [ 1378 | "%%file pairwise_fort.f\n", 1379 | "\n", 1380 | " subroutine pairwise_fort(X,D,m,n)\n", 1381 | " integer :: n,m\n", 1382 | " double precision, intent(in) :: X(m,n)\n", 1383 | " double precision, intent(out) :: D(m,m) \n", 1384 | " integer :: i,j,k\n", 1385 | " double precision :: r \n", 1386 | " do i = 1,m \n", 1387 | " do j = 1,m \n", 1388 | " r = 0\n", 1389 | " do k = 1,n \n", 1390 | " r = r + (X(i,k) - X(j,k)) * (X(i,k) - X(j,k)) \n", 1391 | " end do \n", 1392 | " D(i,j) = sqrt(r) \n", 1393 | " end do \n", 1394 | " end do \n", 1395 | " end subroutine pairwise_fort" 1396 | ], 1397 | "execution_count": null, 1398 | "outputs": [ 1399 | { 1400 | "output_type": "stream", 1401 | "text": [ 1402 | "Writing pairwise_fort.f\n" 1403 | ], 1404 | "name": "stdout" 1405 | } 1406 | ] 1407 | }, 1408 | { 1409 | "cell_type": "code", 1410 | "metadata": { 1411 | "id": "imjbK-PHOpp2" 1412 | }, 1413 | "source": [ 1414 | "# Compile the Fortran with f2py.\n", 1415 | "#!f2py -c pairwise_fort.f -m pairwise_fort --quiet\n", 1416 | "!python -m numpy.f2py -c pairwise_fort.f -m pairwise_fort --quiet" 1417 | ], 1418 | "execution_count": null, 1419 | "outputs": [] 1420 | }, 1421 | { 1422 | "cell_type": "markdown", 1423 | "metadata": { 1424 | "id": "8yBWjzdQOpp2" 1425 | }, 1426 | "source": [ 1427 | "To make sure to be fair, we'll first convert the test array to Fortran-ordering so that no conversion needs to happen in the background:" 1428 | ] 1429 | }, 1430 | { 1431 | "cell_type": "code", 1432 | "metadata": { 1433 | "colab": { 1434 | "base_uri": "https://localhost:8080/" 1435 | }, 1436 | "id": "9ynaA5pQOpp3", 1437 | "outputId": "bc2c8753-cfc5-42f6-a9ca-044aa324cdbd" 1438 | }, 1439 | "source": [ 1440 | "from pairwise_fort import pairwise_fort\n", 1441 | "XF = np.asarray(X, order='F')\n", 1442 | "%timeit pairwise_fort(XF)" 1443 | ], 1444 | "execution_count": null, 1445 | "outputs": [ 1446 | { 1447 | "output_type": "stream", 1448 | "text": [ 1449 | "100 loops, best of 5: 9.65 ms per loop\n" 1450 | ], 1451 | "name": "stdout" 1452 | } 1453 | ] 1454 | }, 1455 | { 1456 | "cell_type": "markdown", 1457 | "metadata": { 1458 | "id": "6W1YoJz1Opp3" 1459 | }, 1460 | "source": [ 1461 | "## Scipy Pairwise Distances" 1462 | ] 1463 | }, 1464 | { 1465 | "cell_type": "code", 1466 | "metadata": { 1467 | "colab": { 1468 | "base_uri": "https://localhost:8080/" 1469 | }, 1470 | "id": "BvB2sGTcOpp3", 1471 | "outputId": "5abd3152-ea90-46e6-eabb-3080f4220892" 1472 | }, 1473 | "source": [ 1474 | "from scipy.spatial.distance import cdist\n", 1475 | "%timeit cdist(X, X)" 1476 | ], 1477 | "execution_count": null, 1478 | "outputs": [ 1479 | { 1480 | "output_type": "stream", 1481 | "text": [ 1482 | "100 loops, best of 5: 3.81 ms per loop\n" 1483 | ], 1484 | "name": "stdout" 1485 | } 1486 | ] 1487 | }, 1488 | { 1489 | "cell_type": "markdown", 1490 | "metadata": { 1491 | "id": "pBORx_ZcOpp4" 1492 | }, 1493 | "source": [ 1494 | "## Scikit-learn Pairwise Distances" 1495 | ] 1496 | }, 1497 | { 1498 | "cell_type": "markdown", 1499 | "metadata": { 1500 | "id": "OyXmIVJHOpp4" 1501 | }, 1502 | "source": [ 1503 | "Scikit-learn contains the ``euclidean_distances`` function, works on sparse\n", 1504 | "matrices as well as numpy arrays, and is implemented in Cython:" 1505 | ] 1506 | }, 1507 | { 1508 | "cell_type": "code", 1509 | "metadata": { 1510 | "colab": { 1511 | "base_uri": "https://localhost:8080/" 1512 | }, 1513 | "id": "qa_UOMw2Opp5", 1514 | "outputId": "89786142-4f6d-4fc8-869b-8050705f6470" 1515 | }, 1516 | "source": [ 1517 | "from sklearn.metrics import euclidean_distances\n", 1518 | "%timeit euclidean_distances(X, X)" 1519 | ], 1520 | "execution_count": null, 1521 | "outputs": [ 1522 | { 1523 | "output_type": "stream", 1524 | "text": [ 1525 | "100 loops, best of 5: 11.7 ms per loop\n" 1526 | ], 1527 | "name": "stdout" 1528 | } 1529 | ] 1530 | }, 1531 | { 1532 | "cell_type": "markdown", 1533 | "metadata": { 1534 | "id": "u17DBRHAOpp5" 1535 | }, 1536 | "source": [ 1537 | "## Comparing the Results" 1538 | ] 1539 | }, 1540 | { 1541 | "cell_type": "code", 1542 | "metadata": { 1543 | "colab": { 1544 | "base_uri": "https://localhost:8080/" 1545 | }, 1546 | "id": "CMQ0xEihOpp5", 1547 | "outputId": "5f3b070c-b4c9-4d99-edd5-fcd38281a92f" 1548 | }, 1549 | "source": [ 1550 | "%pylab inline" 1551 | ], 1552 | "execution_count": null, 1553 | "outputs": [ 1554 | { 1555 | "output_type": "stream", 1556 | "text": [ 1557 | "Populating the interactive namespace from numpy and matplotlib\n" 1558 | ], 1559 | "name": "stdout" 1560 | } 1561 | ] 1562 | }, 1563 | { 1564 | "cell_type": "code", 1565 | "metadata": { 1566 | "colab": { 1567 | "base_uri": "https://localhost:8080/", 1568 | "height": 510 1569 | }, 1570 | "id": "Fvr7h-ziOpp6", 1571 | "outputId": "f93e9595-f994-4c8e-81b2-28bf2d70261e" 1572 | }, 1573 | "source": [ 1574 | "labels = ['python\\nloop', 'pypy', 'numpy\\nbroadc.', 'numexpr', 'sklearn', 'f2py', 'scipy', 'numba\\nCPU', 'cython', 'pythran']\n", 1575 | "#timings = [13.4, 0.0419, 0.017, 0.0167, 0.0129, 0.00987, 0.00912]\n", 1576 | "timings = [5.77, 0.191, 0.0394, 0.0208, 0.0117, 0.00967, 0.00418, 0.0041, 0.00404, 0.00379]\n", 1577 | "spdtimes = []\n", 1578 | "for timing in timings: spdtimes.append(timings[0]/timing)\n", 1579 | "x = np.arange(len(labels))\n", 1580 | "\n", 1581 | "plt.figure(figsize=(15, 8))\n", 1582 | "#ax = plt.axes(xticks=x, yscale='log')\n", 1583 | "ax = plt.axes(xticks=x)\n", 1584 | "#ax.bar(x, timings, width=0.5, alpha=0.4, bottom=1E-6)\n", 1585 | "ax.bar(x, spdtimes, width=0.5, alpha=0.4, bottom=0)\n", 1586 | "ax.grid(which='major', axis='y')\n", 1587 | "ax.set_xlim(-0.5, len(labels) - 0.5)\n", 1588 | "#ax.set_ylim(1E-3, 1E1)\n", 1589 | "ax.xaxis.set_major_formatter(plt.FuncFormatter(lambda i, loc: labels[int(i)]))\n", 1590 | "#ax.set_ylabel('time (s)')\n", 1591 | "#ax.set_ylabel('Speed-up times')\n", 1592 | "ytick_labels = ['1X', '250X', '500X', '750X', '1000X', '1250X', '1500X', '1750X', '2000X']\n", 1593 | "ax.set_yticklabels(ytick_labels)\n", 1594 | "ax.set_title(\"Pairwise Distance Computation Speed-Up Performance\")\n", 1595 | "plt.show()" 1596 | ], 1597 | "execution_count": null, 1598 | "outputs": [ 1599 | { 1600 | "output_type": "display_data", 1601 | "data": { 1602 | "image/png": "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\n", 1603 | "text/plain": [ 1604 | "
" 1605 | ] 1606 | }, 1607 | "metadata": { 1608 | "tags": [], 1609 | "needs_background": "light" 1610 | } 1611 | } 1612 | ] 1613 | } 1614 | ] 1615 | } -------------------------------------------------------------------------------- /speedingloops.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "provenance": [], 7 | "toc_visible": true 8 | }, 9 | "kernelspec": { 10 | "name": "python3", 11 | "display_name": "Python 3" 12 | }, 13 | "language_info": { 14 | "name": "python" 15 | } 16 | }, 17 | "cells": [ 18 | { 19 | "cell_type": "markdown", 20 | "source": [ 21 | "# Not-So-Effective Speeding Tricks" 22 | ], 23 | "metadata": { 24 | "id": "4IOz8xjGHGGp" 25 | } 26 | }, 27 | { 28 | "cell_type": "code", 29 | "source": [ 30 | "count = 100000" 31 | ], 32 | "metadata": { 33 | "id": "vfw3zqwgiKmJ" 34 | }, 35 | "execution_count": 13, 36 | "outputs": [] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "source": [ 41 | "n = range(count)" 42 | ], 43 | "metadata": { 44 | "id": "tA_KqQVsdG0Z" 45 | }, 46 | "execution_count": 14, 47 | "outputs": [] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "source": [ 52 | "## Using List Comprehension" 53 | ], 54 | "metadata": { 55 | "id": "aoFzcoemg1TG" 56 | } 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 16, 61 | "metadata": { 62 | "id": "9a9UpV4Zcywh" 63 | }, 64 | "outputs": [], 65 | "source": [ 66 | "# Baseline version (Inefficient way)\n", 67 | "# Calculating the power of numbers\n", 68 | "# Without using List Comprehension\n", 69 | "def test_01_v0(numbers):\n", 70 | " output = []\n", 71 | " for n in numbers:\n", 72 | " output.append(n ** 2.5)\n", 73 | " return output\n", 74 | "\n", 75 | "# Improved version\n", 76 | "# (Using List Comprehension)\n", 77 | "def test_01_v1(numbers):\n", 78 | " output = [n ** 2.5 for n in numbers]\n", 79 | " return output" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "source": [ 85 | "res_v0 = %timeit -o test_01_v0(n)" 86 | ], 87 | "metadata": { 88 | "colab": { 89 | "base_uri": "https://localhost:8080/" 90 | }, 91 | "id": "yufJs_qGc-go", 92 | "outputId": "00b8fc10-96d8-48b7-b59c-61fac4d6008d" 93 | }, 94 | "execution_count": 17, 95 | "outputs": [ 96 | { 97 | "output_type": "stream", 98 | "name": "stdout", 99 | "text": [ 100 | "18.9 ms ± 4.49 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" 101 | ] 102 | } 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "source": [ 108 | "res_v1 = %timeit -o test_01_v1(n)" 109 | ], 110 | "metadata": { 111 | "colab": { 112 | "base_uri": "https://localhost:8080/" 113 | }, 114 | "id": "Ory0KfCwdL-A", 115 | "outputId": "dd39918b-8eb0-471c-b10a-e73fd8e3981c" 116 | }, 117 | "execution_count": 18, 118 | "outputs": [ 119 | { 120 | "output_type": "stream", 121 | "name": "stdout", 122 | "text": [ 123 | "16 ms ± 4.05 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" 124 | ] 125 | } 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "source": [ 131 | "speedup = res_v0.average / res_v1.average\n", 132 | "print('Speed up: ', speedup)" 133 | ], 134 | "metadata": { 135 | "colab": { 136 | "base_uri": "https://localhost:8080/" 137 | }, 138 | "id": "vCTzrBLseGmi", 139 | "outputId": "02f8775b-d258-4cf0-ce79-0432dc9ee96d" 140 | }, 141 | "execution_count": 19, 142 | "outputs": [ 143 | { 144 | "output_type": "stream", 145 | "name": "stdout", 146 | "text": [ 147 | "Speed up: 1.1846446179724033\n" 148 | ] 149 | } 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "source": [ 155 | "## Using pre-calculated list length" 156 | ], 157 | "metadata": { 158 | "id": "8HQBptEegWrt" 159 | } 160 | }, 161 | { 162 | "cell_type": "code", 163 | "source": [ 164 | "# Baseline version (Inefficient way)\n", 165 | "# (Length calculation inside for loop)\n", 166 | "def test_02_v0(numbers):\n", 167 | " output_list = []\n", 168 | " for i in range(len(numbers)):\n", 169 | " output_list.append(i * 2)\n", 170 | " return output_list\n", 171 | "\n", 172 | "# Improved version\n", 173 | "# (Length calculation outside for loop)\n", 174 | "def test_02_v1(numbers):\n", 175 | " my_list_length = len(numbers)\n", 176 | " output_list = []\n", 177 | " for i in range(my_list_length):\n", 178 | " output_list.append(i * 2)\n", 179 | " return output_list" 180 | ], 181 | "metadata": { 182 | "id": "6e3tiFjpfn_B" 183 | }, 184 | "execution_count": 22, 185 | "outputs": [] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "source": [ 190 | "res02_v0 = %timeit -o test_02_v0(n)" 191 | ], 192 | "metadata": { 193 | "colab": { 194 | "base_uri": "https://localhost:8080/" 195 | }, 196 | "id": "OcoqDPOLghX6", 197 | "outputId": "9dd76f33-f46d-44b2-959f-d6a5b613cd50" 198 | }, 199 | "execution_count": 23, 200 | "outputs": [ 201 | { 202 | "output_type": "stream", 203 | "name": "stdout", 204 | "text": [ 205 | "12.1 ms ± 1.57 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" 206 | ] 207 | } 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "source": [ 213 | "res02_v1 = %timeit -o test_02_v1(n)" 214 | ], 215 | "metadata": { 216 | "colab": { 217 | "base_uri": "https://localhost:8080/" 218 | }, 219 | "id": "Ck0lI1vPgn7Z", 220 | "outputId": "fac69219-dce3-4b03-a428-6c5d7554b12f" 221 | }, 222 | "execution_count": 24, 223 | "outputs": [ 224 | { 225 | "output_type": "stream", 226 | "name": "stdout", 227 | "text": [ 228 | "12.5 ms ± 2.76 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" 229 | ] 230 | } 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "source": [ 236 | "speedup = res02_v0.average / res02_v1.average\n", 237 | "print('Speed up: ', speedup)" 238 | ], 239 | "metadata": { 240 | "colab": { 241 | "base_uri": "https://localhost:8080/" 242 | }, 243 | "id": "zl3E1n5tgtw3", 244 | "outputId": "0ca51e24-e26d-4c53-a293-95760cc223d5" 245 | }, 246 | "execution_count": 25, 247 | "outputs": [ 248 | { 249 | "output_type": "stream", 250 | "name": "stdout", 251 | "text": [ 252 | "Speed up: 0.9737854322293894\n" 253 | ] 254 | } 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "source": [ 260 | "## Skipping the unnecessary steps" 261 | ], 262 | "metadata": { 263 | "id": "W6COQwKByZpY" 264 | } 265 | }, 266 | { 267 | "cell_type": "code", 268 | "source": [ 269 | " # Example of inefficient code used to find\n", 270 | " # the first even square in a list of numbers\n", 271 | " def function_do_something(numbers):\n", 272 | " for n in numbers:\n", 273 | " square = n * n\n", 274 | " if square % 2 == 0:\n", 275 | " return square\n", 276 | "\n", 277 | " return None # No even square found\n", 278 | "\n", 279 | " # Example of improved code that\n", 280 | " # finds result without redundant computations\n", 281 | " def function_do_something_v1(numbers):\n", 282 | " even_numbers = [n for n in numbers if n%2==0]\n", 283 | " for n in even_numbers:\n", 284 | " square = n * n\n", 285 | " return square\n", 286 | "\n", 287 | " return None # No even square found" 288 | ], 289 | "metadata": { 290 | "id": "IlGATC_IydSg" 291 | }, 292 | "execution_count": 18, 293 | "outputs": [] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "source": [ 298 | "res03_v0 = %timeit -o function_do_something(n)" 299 | ], 300 | "metadata": { 301 | "colab": { 302 | "base_uri": "https://localhost:8080/" 303 | }, 304 | "id": "KqA-dBI7yfS8", 305 | "outputId": "e8ac610b-b263-4ef7-b779-208cd9560b53" 306 | }, 307 | "execution_count": 28, 308 | "outputs": [ 309 | { 310 | "output_type": "stream", 311 | "name": "stdout", 312 | "text": [ 313 | "307 ns ± 85.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n" 314 | ] 315 | } 316 | ] 317 | }, 318 | { 319 | "cell_type": "code", 320 | "source": [ 321 | "res03_v1 = %timeit -o function_do_something_v1(n)" 322 | ], 323 | "metadata": { 324 | "colab": { 325 | "base_uri": "https://localhost:8080/" 326 | }, 327 | "id": "Nm4aG0oEyy4_", 328 | "outputId": "9f0a2a19-2d59-479d-d399-184a1e782b9e" 329 | }, 330 | "execution_count": 29, 331 | "outputs": [ 332 | { 333 | "output_type": "stream", 334 | "name": "stdout", 335 | "text": [ 336 | "9.93 ms ± 3.14 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" 337 | ] 338 | } 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "source": [ 344 | "speedup = res03_v0.average / res03_v1.average\n", 345 | "print('Speed up: ', speedup)" 346 | ], 347 | "metadata": { 348 | "colab": { 349 | "base_uri": "https://localhost:8080/" 350 | }, 351 | "id": "RHPAaiglzYPx", 352 | "outputId": "f44f574a-5491-4a82-a750-071d2a8781c7" 353 | }, 354 | "execution_count": 30, 355 | "outputs": [ 356 | { 357 | "output_type": "stream", 358 | "name": "stdout", 359 | "text": [ 360 | "Speed up: 3.0943081385634314e-05\n" 361 | ] 362 | } 363 | ] 364 | }, 365 | { 366 | "cell_type": "markdown", 367 | "source": [ 368 | "## Introducing logic" 369 | ], 370 | "metadata": { 371 | "id": "fSuuuibh0szs" 372 | } 373 | }, 374 | { 375 | "cell_type": "code", 376 | "source": [ 377 | " # Example of inefficient code\n", 378 | " # Loop that calls the is_prime function n times.\n", 379 | " def is_prime(n):\n", 380 | " if n <= 1:\n", 381 | " return False\n", 382 | " for i in range(2, int(n**0.5) + 1):\n", 383 | " if n % i == 0:\n", 384 | " return False\n", 385 | "\n", 386 | " return True\n", 387 | "\n", 388 | " def test_05_v0(n):\n", 389 | " # Baseline version (Inefficient way)\n", 390 | " # (calls the is_prime function n times)\n", 391 | " count = 0\n", 392 | " for i in range(2, n + 1):\n", 393 | " if is_prime(i):\n", 394 | " count += 1\n", 395 | " return count\n", 396 | "\n", 397 | " def test_05_v1(n):\n", 398 | " # Improved version\n", 399 | " # (inlines the logic of the is_prime function)\n", 400 | " count = 0\n", 401 | " for i in range(2, n + 1):\n", 402 | " if i <= 1:\n", 403 | " continue\n", 404 | " for j in range(2, int(i**0.5) + 1):\n", 405 | " if i % j == 0:\n", 406 | " break\n", 407 | " else:\n", 408 | " count += 1\n", 409 | " return count" 410 | ], 411 | "metadata": { 412 | "id": "QZZV1UZS0wU_" 413 | }, 414 | "execution_count": 31, 415 | "outputs": [] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "source": [ 420 | "res05_v0 = %timeit -o test_05_v0(count)" 421 | ], 422 | "metadata": { 423 | "colab": { 424 | "base_uri": "https://localhost:8080/" 425 | }, 426 | "id": "VzLMfmr20xvL", 427 | "outputId": "9eb92d1f-f2c9-4249-8512-5498bb972ad2" 428 | }, 429 | "execution_count": 33, 430 | "outputs": [ 431 | { 432 | "output_type": "stream", 433 | "name": "stdout", 434 | "text": [ 435 | "232 ms ± 9.27 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" 436 | ] 437 | } 438 | ] 439 | }, 440 | { 441 | "cell_type": "code", 442 | "source": [ 443 | "res05_v1 = %timeit -o test_05_v1(count)" 444 | ], 445 | "metadata": { 446 | "colab": { 447 | "base_uri": "https://localhost:8080/" 448 | }, 449 | "id": "iWmbNUet1A2g", 450 | "outputId": "45fb015d-04f5-4f47-8927-a6a78b561776" 451 | }, 452 | "execution_count": 34, 453 | "outputs": [ 454 | { 455 | "output_type": "stream", 456 | "name": "stdout", 457 | "text": [ 458 | "237 ms ± 16.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" 459 | ] 460 | } 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "source": [ 466 | "speedup = res05_v0.average / res05_v1.average\n", 467 | "print('Speed up: ', speedup)" 468 | ], 469 | "metadata": { 470 | "colab": { 471 | "base_uri": "https://localhost:8080/" 472 | }, 473 | "id": "02j9P4YM1Fcn", 474 | "outputId": "2383051c-3a08-4e7f-8345-d9ab932c277b" 475 | }, 476 | "execution_count": 35, 477 | "outputs": [ 478 | { 479 | "output_type": "stream", 480 | "name": "stdout", 481 | "text": [ 482 | "Speed up: 0.9791221155555204\n" 483 | ] 484 | } 485 | ] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "source": [ 490 | "## Precomputing repetitive values" 491 | ], 492 | "metadata": { 493 | "id": "4FIWjmJr1k7s" 494 | } 495 | }, 496 | { 497 | "cell_type": "code", 498 | "source": [ 499 | " def test_07_v0(n):\n", 500 | " # Example of inefficient code\n", 501 | " # Repetitive calculation within nested loop\n", 502 | " result = 0\n", 503 | " for i in range(n):\n", 504 | " for j in range(n):\n", 505 | " result += i * j\n", 506 | " return result\n", 507 | "\n", 508 | " def test_07_v1(n):\n", 509 | " # Example of improved code\n", 510 | " # Utilize precomputed values to help speedup\n", 511 | " pv = [[i * j for j in range(n)] for i in range(n)]\n", 512 | " result = 0\n", 513 | " for i in range(n):\n", 514 | " result += sum(pv[i][:i+1])\n", 515 | " return result" 516 | ], 517 | "metadata": { 518 | "id": "s5hjvgvz1lsl" 519 | }, 520 | "execution_count": 36, 521 | "outputs": [] 522 | }, 523 | { 524 | "cell_type": "code", 525 | "source": [ 526 | "n1 = 10000" 527 | ], 528 | "metadata": { 529 | "id": "1D62Ef432_Sj" 530 | }, 531 | "execution_count": 43, 532 | "outputs": [] 533 | }, 534 | { 535 | "cell_type": "code", 536 | "source": [ 537 | "res07_v0 = %timeit -o test_07_v0(n1)" 538 | ], 539 | "metadata": { 540 | "colab": { 541 | "base_uri": "https://localhost:8080/" 542 | }, 543 | "id": "8ROVPf881m66", 544 | "outputId": "191824e7-af6c-4f55-9a7e-8b4738a7211b" 545 | }, 546 | "execution_count": 44, 547 | "outputs": [ 548 | { 549 | "output_type": "stream", 550 | "name": "stdout", 551 | "text": [ 552 | "11.3 s ± 379 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" 553 | ] 554 | } 555 | ] 556 | }, 557 | { 558 | "cell_type": "code", 559 | "source": [ 560 | "res07_v1 = %timeit -o test_07_v1(n1)" 561 | ], 562 | "metadata": { 563 | "colab": { 564 | "base_uri": "https://localhost:8080/" 565 | }, 566 | "id": "OKTQaTTZ1sKb", 567 | "outputId": "74b7c326-0164-4fd6-e045-e77e7bdce208" 568 | }, 569 | "execution_count": 45, 570 | "outputs": [ 571 | { 572 | "output_type": "stream", 573 | "name": "stdout", 574 | "text": [ 575 | "12.4 s ± 861 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" 576 | ] 577 | } 578 | ] 579 | }, 580 | { 581 | "cell_type": "code", 582 | "source": [ 583 | "speedup = res07_v0.average / res07_v1.average\n", 584 | "print('Speed up: ', speedup)" 585 | ], 586 | "metadata": { 587 | "colab": { 588 | "base_uri": "https://localhost:8080/" 589 | }, 590 | "id": "aq1h-jcW1_6T", 591 | "outputId": "8f92ed49-7b55-456d-fb6b-9715c7e2f282" 592 | }, 593 | "execution_count": 46, 594 | "outputs": [ 595 | { 596 | "output_type": "stream", 597 | "name": "stdout", 598 | "text": [ 599 | "Speed up: 0.9103769922988914\n" 600 | ] 601 | } 602 | ] 603 | }, 604 | { 605 | "cell_type": "markdown", 606 | "source": [ 607 | "# Very Efficient Speeding Tricks" 608 | ], 609 | "metadata": { 610 | "id": "eqdlczGdHr1M" 611 | } 612 | }, 613 | { 614 | "cell_type": "markdown", 615 | "source": [ 616 | "## Vectorization" 617 | ], 618 | "metadata": { 619 | "id": "6QUTkiELjzWy" 620 | } 621 | }, 622 | { 623 | "cell_type": "code", 624 | "source": [ 625 | " import numpy as np\n", 626 | "\n", 627 | " def test_11_v0(n):\n", 628 | " # Baseline version\n", 629 | " # (Inefficient way of summing numbers in a range)\n", 630 | " output = 0\n", 631 | " for i in range(0, n):\n", 632 | " output = output + i\n", 633 | "\n", 634 | " return output\n", 635 | "\n", 636 | " def test_11_v1(n):\n", 637 | " # Improved version\n", 638 | " # (# Efficient way of summing numbers in a range)\n", 639 | " output = np.sum(np.arange(n))\n", 640 | " return output" 641 | ], 642 | "metadata": { 643 | "id": "S9yAGosojz3X" 644 | }, 645 | "execution_count": 25, 646 | "outputs": [] 647 | }, 648 | { 649 | "cell_type": "code", 650 | "source": [ 651 | "res11_v0 = %timeit -o test_11_v0(count)" 652 | ], 653 | "metadata": { 654 | "colab": { 655 | "base_uri": "https://localhost:8080/" 656 | }, 657 | "id": "_STdfRmEj74G", 658 | "outputId": "08ed90c1-a0bb-4246-b265-5de1de011072" 659 | }, 660 | "execution_count": 26, 661 | "outputs": [ 662 | { 663 | "output_type": "stream", 664 | "name": "stdout", 665 | "text": [ 666 | "6.36 ms ± 89.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" 667 | ] 668 | } 669 | ] 670 | }, 671 | { 672 | "cell_type": "code", 673 | "source": [ 674 | "res11_v1 = %timeit -o test_11_v1(count)" 675 | ], 676 | "metadata": { 677 | "colab": { 678 | "base_uri": "https://localhost:8080/" 679 | }, 680 | "id": "OUGikALCkFKd", 681 | "outputId": "41e55ed8-9556-4533-db31-a5cb57136bbc" 682 | }, 683 | "execution_count": 27, 684 | "outputs": [ 685 | { 686 | "output_type": "stream", 687 | "name": "stdout", 688 | "text": [ 689 | "95.5 µs ± 24.2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" 690 | ] 691 | } 692 | ] 693 | }, 694 | { 695 | "cell_type": "code", 696 | "source": [ 697 | "speedup = res11_v0.average / res11_v1.average\n", 698 | "print('Speed up: ', speedup)" 699 | ], 700 | "metadata": { 701 | "colab": { 702 | "base_uri": "https://localhost:8080/" 703 | }, 704 | "id": "6EIgXj_BlAgi", 705 | "outputId": "8fc54602-6d87-406a-8060-60f3cafda5dd" 706 | }, 707 | "execution_count": 28, 708 | "outputs": [ 709 | { 710 | "output_type": "stream", 711 | "name": "stdout", 712 | "text": [ 713 | "Speed up: 66.59235792009521\n" 714 | ] 715 | } 716 | ] 717 | }, 718 | { 719 | "cell_type": "markdown", 720 | "source": [ 721 | "## Using generator" 722 | ], 723 | "metadata": { 724 | "id": "cEJeDbIcgyDY" 725 | } 726 | }, 727 | { 728 | "cell_type": "code", 729 | "source": [ 730 | " def test_08_v0(n):\n", 731 | " # Baseline version (Inefficient way)\n", 732 | " # (Inefficiently calculates the nth Fibonacci\n", 733 | " # number using a list)\n", 734 | " if n <= 1:\n", 735 | " return n\n", 736 | " f_list = [0, 1]\n", 737 | " for i in range(2, n + 1):\n", 738 | " f_list.append(f_list[i - 1] + f_list[i - 2])\n", 739 | " return f_list[n]\n", 740 | "\n", 741 | " def test_08_v1(n):\n", 742 | " # Improved version\n", 743 | " # (Efficiently calculates the nth Fibonacci\n", 744 | " # number using a generator)\n", 745 | " a, b = 0, 1\n", 746 | " for _ in range(n):\n", 747 | " yield a\n", 748 | " a, b = b, a + b" 749 | ], 750 | "metadata": { 751 | "id": "hFdjqIlbgydF" 752 | }, 753 | "execution_count": null, 754 | "outputs": [] 755 | }, 756 | { 757 | "cell_type": "code", 758 | "source": [ 759 | "res08_v0 = %timeit -o test_08_v0(count)" 760 | ], 761 | "metadata": { 762 | "colab": { 763 | "base_uri": "https://localhost:8080/" 764 | }, 765 | "id": "ow5NCKcHg8LK", 766 | "outputId": "22c4041c-3b15-4bfe-b0cd-ca9c946b5929" 767 | }, 768 | "execution_count": null, 769 | "outputs": [ 770 | { 771 | "output_type": "stream", 772 | "name": "stdout", 773 | "text": [ 774 | "431 ms ± 6.65 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" 775 | ] 776 | } 777 | ] 778 | }, 779 | { 780 | "cell_type": "code", 781 | "source": [ 782 | "res08_v1 = %timeit -o test_08_v1(count)" 783 | ], 784 | "metadata": { 785 | "colab": { 786 | "base_uri": "https://localhost:8080/" 787 | }, 788 | "id": "d3zx4bmfhVBM", 789 | "outputId": "1d6ed0ea-b78e-4a04-a7fe-b755e2a93325" 790 | }, 791 | "execution_count": null, 792 | "outputs": [ 793 | { 794 | "output_type": "stream", 795 | "name": "stdout", 796 | "text": [ 797 | "481 ns ± 43.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n" 798 | ] 799 | } 800 | ] 801 | }, 802 | { 803 | "cell_type": "code", 804 | "source": [ 805 | "speedup = res08_v0.average / res08_v1.average\n", 806 | "print('Speed up: ', speedup)" 807 | ], 808 | "metadata": { 809 | "colab": { 810 | "base_uri": "https://localhost:8080/" 811 | }, 812 | "id": "P0ffiuCehZDc", 813 | "outputId": "b0e0507f-2e49-463d-b94a-3ea4ebd76d21" 814 | }, 815 | "execution_count": null, 816 | "outputs": [ 817 | { 818 | "output_type": "stream", 819 | "name": "stdout", 820 | "text": [ 821 | "Speed up: 895108.6790388676\n" 822 | ] 823 | } 824 | ] 825 | }, 826 | { 827 | "cell_type": "markdown", 828 | "source": [ 829 | "## Using map()" 830 | ], 831 | "metadata": { 832 | "id": "42oFAiAwimbY" 833 | } 834 | }, 835 | { 836 | "cell_type": "code", 837 | "source": [ 838 | " def some_function_X(x):\n", 839 | " # This would normally be a function containing application logic\n", 840 | " # which required it to be made into a separate function\n", 841 | " # (for the purpose of this test, just calculate and return the square)\n", 842 | " return x**2\n", 843 | "\n", 844 | " def test_09_v0(numbers):\n", 845 | " # Baseline version (Inefficient way)\n", 846 | " output = []\n", 847 | " for i in numbers:\n", 848 | " output.append(some_function_X(i))\n", 849 | "\n", 850 | " return output\n", 851 | "\n", 852 | " def test_09_v1(numbers):\n", 853 | " # Improved version\n", 854 | " # (Using Python's built-in map() function)\n", 855 | " output = map(some_function_X, numbers)\n", 856 | " return output" 857 | ], 858 | "metadata": { 859 | "id": "nzw7QRUIhwCs" 860 | }, 861 | "execution_count": null, 862 | "outputs": [] 863 | }, 864 | { 865 | "cell_type": "code", 866 | "source": [ 867 | "res09_v0 = %timeit -o test_09_v0(n)" 868 | ], 869 | "metadata": { 870 | "colab": { 871 | "base_uri": "https://localhost:8080/" 872 | }, 873 | "id": "lV39fAgZi1aP", 874 | "outputId": "10a71962-fad3-456a-aaff-8f061a2145f1" 875 | }, 876 | "execution_count": null, 877 | "outputs": [ 878 | { 879 | "output_type": "stream", 880 | "name": "stdout", 881 | "text": [ 882 | "44.2 ms ± 1.06 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" 883 | ] 884 | } 885 | ] 886 | }, 887 | { 888 | "cell_type": "code", 889 | "source": [ 890 | "res09_v1 = %timeit -o test_09_v1(n)" 891 | ], 892 | "metadata": { 893 | "colab": { 894 | "base_uri": "https://localhost:8080/" 895 | }, 896 | "id": "fGkgKwn9i8LU", 897 | "outputId": "6fa2a828-6114-4584-d4c6-78f3b3dcf5a8" 898 | }, 899 | "execution_count": null, 900 | "outputs": [ 901 | { 902 | "output_type": "stream", 903 | "name": "stdout", 904 | "text": [ 905 | "371 ns ± 91.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n" 906 | ] 907 | } 908 | ] 909 | }, 910 | { 911 | "cell_type": "code", 912 | "source": [ 913 | "speedup = res08_v0.average / res08_v1.average\n", 914 | "print('Speed up: ', speedup)" 915 | ], 916 | "metadata": { 917 | "colab": { 918 | "base_uri": "https://localhost:8080/" 919 | }, 920 | "id": "jZmQFTgHi_4S", 921 | "outputId": "48c8b75c-a79f-4d82-ffdf-a9f8e6a70fa6" 922 | }, 923 | "execution_count": null, 924 | "outputs": [ 925 | { 926 | "output_type": "stream", 927 | "name": "stdout", 928 | "text": [ 929 | "Speed up: 895108.6790388676\n" 930 | ] 931 | } 932 | ] 933 | }, 934 | { 935 | "cell_type": "markdown", 936 | "source": [ 937 | "# \"Make-Or-Break\" Speeding Tricks" 938 | ], 939 | "metadata": { 940 | "id": "bHv5cULaIoxR" 941 | } 942 | }, 943 | { 944 | "cell_type": "markdown", 945 | "source": [ 946 | "## Using filterfalse" 947 | ], 948 | "metadata": { 949 | "id": "J5ck6QoZlfKe" 950 | } 951 | }, 952 | { 953 | "cell_type": "code", 954 | "source": [ 955 | " def test_12_v0(numbers):\n", 956 | " # Baseline version (Inefficient way)\n", 957 | " filtered_data = []\n", 958 | " for i in numbers:\n", 959 | " filtered_data.extend(list(\n", 960 | " filter(lambda x: x % 5 == 0,\n", 961 | " range(1, i**2))))\n", 962 | "\n", 963 | " return filtered_data" 964 | ], 965 | "metadata": { 966 | "id": "0QxrZFTOlftY" 967 | }, 968 | "execution_count": 1, 969 | "outputs": [] 970 | }, 971 | { 972 | "cell_type": "code", 973 | "source": [ 974 | " from itertools import filterfalse\n", 975 | "\n", 976 | " def test_12_v1(numbers):\n", 977 | " # Improved version\n", 978 | " # (using filterfalse)\n", 979 | " filtered_data = []\n", 980 | " for i in numbers:\n", 981 | " filtered_data.extend(list(\n", 982 | " filterfalse(lambda x: x % 5 != 0,\n", 983 | " range(1, i**2))))\n", 984 | "\n", 985 | " return filtered_data" 986 | ], 987 | "metadata": { 988 | "id": "jGyJYqRJljlg" 989 | }, 990 | "execution_count": 2, 991 | "outputs": [] 992 | }, 993 | { 994 | "cell_type": "code", 995 | "source": [ 996 | "n1 = range(1000)" 997 | ], 998 | "metadata": { 999 | "id": "79VNeYfomsL1" 1000 | }, 1001 | "execution_count": 7, 1002 | "outputs": [] 1003 | }, 1004 | { 1005 | "cell_type": "code", 1006 | "source": [ 1007 | "res12_v0 = %timeit -o test_12_v0(n1)" 1008 | ], 1009 | "metadata": { 1010 | "colab": { 1011 | "base_uri": "https://localhost:8080/" 1012 | }, 1013 | "id": "J7Dzgkeolmpm", 1014 | "outputId": "8932bdf6-dc7c-40a5-e44f-861808697bb8" 1015 | }, 1016 | "execution_count": 8, 1017 | "outputs": [ 1018 | { 1019 | "output_type": "stream", 1020 | "name": "stdout", 1021 | "text": [ 1022 | "46.6 s ± 910 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" 1023 | ] 1024 | } 1025 | ] 1026 | }, 1027 | { 1028 | "cell_type": "code", 1029 | "source": [ 1030 | "res12_v1 = %timeit -o test_12_v1(n1)" 1031 | ], 1032 | "metadata": { 1033 | "colab": { 1034 | "base_uri": "https://localhost:8080/" 1035 | }, 1036 | "id": "6xwWzalolu44", 1037 | "outputId": "4031311b-002d-4d34-eff4-9d9f7ef30024" 1038 | }, 1039 | "execution_count": 9, 1040 | "outputs": [ 1041 | { 1042 | "output_type": "stream", 1043 | "name": "stdout", 1044 | "text": [ 1045 | "932 ns ± 25.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n" 1046 | ] 1047 | } 1048 | ] 1049 | }, 1050 | { 1051 | "cell_type": "code", 1052 | "source": [ 1053 | "speedup = res12_v0.average / res12_v1.average\n", 1054 | "print('Speed up: ', speedup)" 1055 | ], 1056 | "metadata": { 1057 | "colab": { 1058 | "base_uri": "https://localhost:8080/" 1059 | }, 1060 | "id": "BiLBvtQDlwks", 1061 | "outputId": "ffc1ac52-c462-40dd-fb44-1f8bb3635e1e" 1062 | }, 1063 | "execution_count": 10, 1064 | "outputs": [ 1065 | { 1066 | "output_type": "stream", 1067 | "name": "stdout", 1068 | "text": [ 1069 | "Speed up: 50010798.64917214\n" 1070 | ] 1071 | } 1072 | ] 1073 | }, 1074 | { 1075 | "cell_type": "markdown", 1076 | "source": [ 1077 | "## Using Memoization" 1078 | ], 1079 | "metadata": { 1080 | "id": "F2YFSCNAjIEx" 1081 | } 1082 | }, 1083 | { 1084 | "cell_type": "code", 1085 | "source": [ 1086 | " # Example of inefficient code\n", 1087 | " def fibonacci(n):\n", 1088 | " if n == 0:\n", 1089 | " return 0\n", 1090 | " elif n == 1:\n", 1091 | " return 1\n", 1092 | " return fibonacci(n - 1) + fibonacci(n-2)\n", 1093 | "\n", 1094 | " def test_10_v0(numbers):\n", 1095 | " output = []\n", 1096 | " for i in numbers:\n", 1097 | " output.append(fibonacci(i))\n", 1098 | "\n", 1099 | " return output" 1100 | ], 1101 | "metadata": { 1102 | "id": "91k20yQzjCGw" 1103 | }, 1104 | "execution_count": 1, 1105 | "outputs": [] 1106 | }, 1107 | { 1108 | "cell_type": "code", 1109 | "source": [ 1110 | " # Example of efficient code\n", 1111 | " # Using Python's functools' lru_cache function\n", 1112 | " import functools\n", 1113 | "\n", 1114 | " @functools.lru_cache()\n", 1115 | " def fibonacci_v2(n):\n", 1116 | " if n == 0:\n", 1117 | " return 0\n", 1118 | " elif n == 1:\n", 1119 | " return 1\n", 1120 | " return fibonacci_v2(n - 1) + fibonacci_v2(n-2)\n", 1121 | "\n", 1122 | " def test_10_v1(numbers):\n", 1123 | " output = []\n", 1124 | " for i in numbers:\n", 1125 | " output.append(fibonacci_v2(i))\n", 1126 | "\n", 1127 | " return output" 1128 | ], 1129 | "metadata": { 1130 | "id": "DX9quRKzjMkw" 1131 | }, 1132 | "execution_count": 2, 1133 | "outputs": [] 1134 | }, 1135 | { 1136 | "cell_type": "code", 1137 | "source": [ 1138 | "n1 = range(40)" 1139 | ], 1140 | "metadata": { 1141 | "id": "32KY2YHtnL4s" 1142 | }, 1143 | "execution_count": 3, 1144 | "outputs": [] 1145 | }, 1146 | { 1147 | "cell_type": "code", 1148 | "source": [ 1149 | "test_10_v0(n1)" 1150 | ], 1151 | "metadata": { 1152 | "colab": { 1153 | "base_uri": "https://localhost:8080/" 1154 | }, 1155 | "id": "YzrpFnhWmLfn", 1156 | "outputId": "6931f580-6995-4ce2-a18f-660c5cda1e0a" 1157 | }, 1158 | "execution_count": 4, 1159 | "outputs": [ 1160 | { 1161 | "output_type": "execute_result", 1162 | "data": { 1163 | "text/plain": [ 1164 | "[0,\n", 1165 | " 1,\n", 1166 | " 1,\n", 1167 | " 2,\n", 1168 | " 3,\n", 1169 | " 5,\n", 1170 | " 8,\n", 1171 | " 13,\n", 1172 | " 21,\n", 1173 | " 34,\n", 1174 | " 55,\n", 1175 | " 89,\n", 1176 | " 144,\n", 1177 | " 233,\n", 1178 | " 377,\n", 1179 | " 610,\n", 1180 | " 987,\n", 1181 | " 1597,\n", 1182 | " 2584,\n", 1183 | " 4181,\n", 1184 | " 6765,\n", 1185 | " 10946,\n", 1186 | " 17711,\n", 1187 | " 28657,\n", 1188 | " 46368,\n", 1189 | " 75025,\n", 1190 | " 121393,\n", 1191 | " 196418,\n", 1192 | " 317811,\n", 1193 | " 514229,\n", 1194 | " 832040,\n", 1195 | " 1346269,\n", 1196 | " 2178309,\n", 1197 | " 3524578,\n", 1198 | " 5702887,\n", 1199 | " 9227465,\n", 1200 | " 14930352,\n", 1201 | " 24157817,\n", 1202 | " 39088169,\n", 1203 | " 63245986]" 1204 | ] 1205 | }, 1206 | "metadata": {}, 1207 | "execution_count": 4 1208 | } 1209 | ] 1210 | }, 1211 | { 1212 | "cell_type": "code", 1213 | "source": [ 1214 | "res10_v0 = %timeit -r 1 -n 1 -o test_10_v0(n1)" 1215 | ], 1216 | "metadata": { 1217 | "id": "SlNms5pyjQVq", 1218 | "colab": { 1219 | "base_uri": "https://localhost:8080/" 1220 | }, 1221 | "outputId": "ca67b9d2-817c-4713-fa38-b57fc65e1dc3" 1222 | }, 1223 | "execution_count": 5, 1224 | "outputs": [ 1225 | { 1226 | "output_type": "stream", 1227 | "name": "stdout", 1228 | "text": [ 1229 | "1min 30s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" 1230 | ] 1231 | } 1232 | ] 1233 | }, 1234 | { 1235 | "cell_type": "code", 1236 | "source": [ 1237 | "res10_v1 = %timeit -o test_10_v1(n1)" 1238 | ], 1239 | "metadata": { 1240 | "colab": { 1241 | "base_uri": "https://localhost:8080/" 1242 | }, 1243 | "id": "MRJheqGqjqvv", 1244 | "outputId": "7bbd1a65-d6e2-40b9-c191-e51a89caecda" 1245 | }, 1246 | "execution_count": 6, 1247 | "outputs": [ 1248 | { 1249 | "output_type": "stream", 1250 | "name": "stdout", 1251 | "text": [ 1252 | "5.16 µs ± 98 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n" 1253 | ] 1254 | } 1255 | ] 1256 | }, 1257 | { 1258 | "cell_type": "code", 1259 | "source": [ 1260 | "speedup = res10_v0.average / res10_v1.average\n", 1261 | "print('Speed up: ', speedup)" 1262 | ], 1263 | "metadata": { 1264 | "id": "QOmBrz_GjwMt", 1265 | "colab": { 1266 | "base_uri": "https://localhost:8080/" 1267 | }, 1268 | "outputId": "cb97793a-1396-434e-dd65-12f9056252a4" 1269 | }, 1270 | "execution_count": 7, 1271 | "outputs": [ 1272 | { 1273 | "output_type": "stream", 1274 | "name": "stdout", 1275 | "text": [ 1276 | "Speed up: 17490043.17146158\n" 1277 | ] 1278 | } 1279 | ] 1280 | } 1281 | ] 1282 | } --------------------------------------------------------------------------------