{
 "metadata": {
  "name": "sum_primes"
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "#Parallel Code using pp Library \n",
      "\n",
      "Parallel Python Software\n",
      "[www.parallelpython.com](www.parallelpython.com)\n",
      "##Overview: \n",
      "\n",
      "PP is a python module which provides mechanism for parallel execution of python code on SMP \n",
      "(systems with multiple processors or cores) and clusters (computers connected via network).\n",
      "\n",
      "It is light, easy to install and integrate with other python software.\n",
      "\n",
      "PP is an open source and cross-platform module written in pure python \n",
      "\n",
      "##Features: \n",
      "\n",
      "* Parallel execution of python code on SMP and clusters\n",
      "* Easy to understand and implement job-based parallelization technique (easy to convert serial application in parallel)\n",
      "* Automatic detection of the optimal configuration (by default the number of worker processes is set to the number of effective processors)\n",
      "* Dynamic processors allocation (number of worker processes can be changed at runtime)\n",
      "* Low overhead for subsequent jobs with the same function (transparent caching is implemented to decrease the overhead)\n",
      "* Dynamic load balancing (jobs are distributed between processors at runtime)\n",
      "* Fault-tolerance (if one of the nodes fails tasks are rescheduled on others)\n",
      "* Auto-discovery of computational resources\n",
      "* Dynamic allocation of computational resources (consequence of auto-discovery and fault-tolerance)\n",
      "* SHA based authentication for network connections\n",
      "* Cross-platform portability and interoperability (Windows, Linux, Unix, Mac OS X)\n",
      "* Cross-architecture portability and interoperability (x86, x86-64, etc.)\n",
      "* Open source \n",
      "\n",
      "##Motivation: \n",
      "\n",
      "Nowadays software written in python finds applications in broad range of the categories including business logic, \n",
      "data analysis and scientific calculations. This together with wide availability of SMP computers \n",
      "(multi-processor or multi-core) and clusters (computers connected via network) on the market create \n",
      "the demand in parallel execution of python code. \n",
      "\n",
      "The most simple and common way to write parallel applications for SMP computers is to use threads. \n",
      "Although, it appears that if the application is computation-bound using 'thread' or 'threading' \n",
      "python modules will not allow to run python byte-code in parallel. The reason is that python \n",
      "interpreter uses GIL (Global Interpreter Lock) for internal bookkeeping. This lock allows to \n",
      "execute only one python byte-code instruction at a time even on an SMP computer.\n",
      "\n",
      "PP module overcomes this limitation and provides a simple way to write parallel python applications. \n",
      "Internally ppsmp  uses processes and IPC (Inter Process Communications) to organize parallel computations. \n",
      "All the details and complexity of the latter are completely taken care of, and your application \n",
      "just submits jobs and retrieves their results (the easiest way to write parallel applications).\n",
      "\n",
      "To make things even better, the software written with PP works in parallel even on many computers connected \n",
      "via local network or Internet. Cross-platform portability and dynamic load-balancing allows PP to \n",
      "parallelize computations efficiently even on heterogeneous and multi-platform clusters. \n",
      "\n",
      "##Installation: \n",
      "\n",
      "Any platform: download a module archive and extract it to a local directory. Run the setup script: python setup.py install \n",
      "\n",
      "Windows: download and execute windows installer binary. \n",
      "\n",
      "##Documentation:\n",
      "\n",
      "    Module API \n",
      "    Quick start guide, SMP\n",
      "    Quick start guide, clusters\n",
      "    Advanced guide, clusters\n",
      "    Command line options, ppserver.py\n",
      "    PP FAQ \n",
      "\n",
      "##Examples:\n",
      "\n",
      "    Parallel Python usage examples\n",
      "\n",
      "##Download: \n",
      "\n",
      "    Parallel Python downloads"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "#Quick start guide, SMP\n",
      "\n",
      "1. Import pp module:\n",
      "\n",
      "    import pp\n",
      "\n",
      "2. Start pp execution server with the number of workers set to the number of processors in the system\n",
      "\n",
      "    job_server = pp.Server() \n",
      "\n",
      "3. Submit all the tasks for parallel execution:\n",
      "\n",
      "    f1 = job_server.submit(func1, args1, depfuncs1, modules1)\n",
      "\n",
      "    f2 = job_server.submit(func1, args2, depfuncs1, modules1)\n",
      "\n",
      "    f3 = job_server.submit(func2, args3, depfuncs2, modules2) \n",
      "   ...etc...\n",
      "\n",
      "4. Retrieve the results as needed:\n",
      "\n",
      "    r1 = f1()\n",
      "\n",
      "    r2 = f2()\n",
      "\n",
      "    r3 = f3() \n",
      "\n",
      "    ...etc...\n",
      "\n",
      " To find out how to achieve efficient parallelization with pp please take a look at example below\n",
      " "
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Example 1: sum_primes.py  \n",
      "\n",
      "#!/usr/bin/python\n",
      "# File: sum_primes.py\n",
      "# Author: VItalii Vanovschi\n",
      "# Desc: This program demonstrates parallel computations with pp module\n",
      "# It calculates the sum of prime numbers below a given integer in parallel\n",
      "# Parallel Python Software: http://www.parallelpython.com\n",
      "\n",
      "import math, sys, time\n",
      "import pp\n",
      "\n",
      "def isprime(n):\n",
      "    \"\"\"Returns True if n is prime and False otherwise\"\"\"\n",
      "    if not isinstance(n, int):\n",
      "        raise TypeError(\"argument passed to is_prime is not of 'int' type\")\n",
      "    if n < 2:\n",
      "        return False\n",
      "    if n == 2:\n",
      "        return True\n",
      "    max = int(math.ceil(math.sqrt(n)))\n",
      "    i = 2\n",
      "    while i <= max:\n",
      "        if n % i == 0:\n",
      "            return False\n",
      "        i += 1\n",
      "    return True\n",
      "\n",
      "def sum_primes(n):\n",
      "    \"\"\"Calculates sum of all primes below given integer n\"\"\"\n",
      "    return sum([x for x in xrange(2,n) if isprime(x)])\n",
      "\n",
      "print \"\"\"Usage: python sum_primes.py [ncpus]\n",
      "    [ncpus] - the number of workers to run in parallel,\n",
      "    if omitted it will be set to the number of processors in the system\n",
      "\"\"\"\n",
      "\n",
      "# tuple of all parallel python servers to connect with\n",
      "ppservers = ()\n",
      "# Creates jobserver with automatically detected number of workers\n",
      "job_server=pp.Server(ppservers=ppservers)\n",
      "#ppservers = (\"10.0.0.1\",)\n",
      "\n",
      "print \"Starting pp with\", job_server.get_ncpus(), \"workers\"\n",
      "\n",
      "# Submit a job of calulating sum_primes(100) for execution. \n",
      "# sum_primes - the function\n",
      "# (100,) - tuple with arguments for sum_primes\n",
      "# (isprime,) - tuple with functions on which function sum_primes depends\n",
      "# (\"math\",) - tuple with module names which must be imported before sum_primes execution\n",
      "# Execution starts as soon as one of the workers will become available\n",
      "job1 = job_server.submit(sum_primes, (100,), (isprime,), (\"math\",))\n",
      "\n",
      "# Retrieves the result calculated by job1\n",
      "# The value of job1() is the same as sum_primes(100)\n",
      "# If the job has not been finished yet, execution will wait here until result is available\n",
      "result = job1()\n",
      "\n",
      "print \"Sum of primes below 100 is\", result\n",
      "\n",
      "start_time = time.time()\n",
      "\n",
      "# The following submits 8 jobs and then retrieves the results\n",
      "inputs = (100000, 100100, 100200, 100300, 100400, 100500, 100600, 100700)\n",
      "jobs = [(input, job_server.submit(sum_primes,(input,), (isprime,), (\"math\",))) for input in inputs]\n",
      "for input, job in jobs:\n",
      "    print \"Sum of primes below\", input, \"is\", job()\n",
      "\n",
      "print \"Time elapsed: \", time.time() - start_time, \"s\"\n",
      "job_server.print_stats()\n",
      "\n",
      "# Parallel Python Software: http://www.parallelpython.com\n"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Usage: python sum_primes.py [ncpus]\n",
        "    [ncpus] - the number of workers to run in parallel,\n",
        "    if omitted it will be set to the number of processors in the system\n",
        "\n",
        "Starting pp with"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 4 workers\n",
        "Sum of primes below 100 is 1060\n",
        "Sum of primes below"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 100000 is "
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "454396537\n",
        "Sum of primes below 100100 is 454996777\n",
        "Sum of primes below 100200 is 455898156\n",
        "Sum of primes below 100300 is 456700218\n",
        "Sum of primes below 100400 is "
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "457603451\n",
        "Sum of primes below 100500 is "
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "458407033\n",
        "Sum of primes below 100600 is 459412387\n",
        "Sum of primes below 100700 is 460217613\n",
        "Time elapsed:  2.59499979019 s\n",
        "Job execution statistics:\n",
        " job count | % of all jobs | job time sum | time per job | job server\n",
        "         9 |        100.00 |       9.9480 |     1.105333 | local\n",
        "Time elapsed since server creation 2.60799980164\n",
        "\n"
       ]
      }
     ],
     "prompt_number": 1
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Parallel IPython Code"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import os\n",
      "from IPython.core.display import display\n",
      "from IPython import parallel\n",
      "rc = parallel.Client()\n",
      "dv = rc[:]\n",
      "rc.ids\n",
      "view=rc.load_balanced_view()\n",
      "view.block=False"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 2
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import math, sys, time\n",
      "%px import math"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 3
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%px\n",
      "def isprime(n):\n",
      "    \"\"\"Returns True if n is prime and False otherwise\"\"\"\n",
      "    if not isinstance(n, int):\n",
      "        raise TypeError(\"argument passed to is_prime is not of 'int' type\")\n",
      "    if n < 2:\n",
      "        return False\n",
      "    if n == 2:\n",
      "        return True\n",
      "    max = int(math.ceil(math.sqrt(n)))\n",
      "    i = 2\n",
      "    while i <= max:\n",
      "        if n % i == 0:\n",
      "            return False\n",
      "        i += 1\n",
      "    return True\n",
      "def sum_primes(n):\n",
      "    global isprime\n",
      "    \"\"\"Calculates sum of all primes below given integer n\"\"\"\n",
      "    return sum([x for x in xrange(2,n) if isprime(x)])\n",
      "\n"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 4
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "print \"Starting parallel ipython with\", len(dv), \"workers\"\n",
      "\n",
      "# Submit a job of calulating sum_primes(100) for execution. \n",
      "# sum_primes - the function\n",
      "# (100,) - tuple with arguments for sum_primes\n",
      "# (isprime,) - tuple with functions on which function sum_primes depends\n",
      "# (\"math\",) - tuple with module names which must be imported before sum_primes execution\n",
      "# Execution starts as soon as one of the workers will become available\n",
      "job1 = results = view.map_async(sum_primes, (100,))\n",
      "\n",
      "# Retrieves the result calculated by job1\n",
      "# The value of job1() is the same as sum_primes(100)\n",
      "# If the job has not been finished yet, execution will wait here until result is available\n",
      "result = list(job1)\n",
      "\n",
      "print \"Sum of primes below 100 is\", result\n",
      "\n",
      "start_time = time.time()\n",
      "\n",
      "# The following submits 8 jobs and then retrieves the results\n",
      "inputs = (100000, 100100, 100200, 100300, 100400, 100500, 100600, 100700)\n",
      "jobs = view.map_async(sum_primes,inputs)\n",
      "results=list(jobs)\n",
      "print results\n",
      "index=0\n",
      "for input in inputs:\n",
      "    print \"Sum of primes below\", input, \"is\", results[index:index+1]\n",
      "    index=index+1\n",
      "print \"Time elapsed: \", time.time() - start_time, \"s\"\n",
      "job_server.print_stats()\n"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Starting parallel ipython with 4 workers\n",
        "Sum of primes below 100 is [1060]\n",
        "[454396537, 454996777, 455898156, 456700218, 457603451, 458407033, 459412387, 460217613]"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "\n",
        "Sum of primes below 100000 is [454396537]\n",
        "Sum of primes below 100100 is [454996777]\n",
        "Sum of primes below 100200 is [455898156]\n",
        "Sum of primes below 100300 is [456700218]\n",
        "Sum of primes below 100400 is [457603451]\n",
        "Sum of primes below 100500 is [458407033]\n",
        "Sum of primes below 100600 is [459412387]\n",
        "Sum of primes below 100700 is [460217613]\n",
        "Time elapsed:  1.68700003624 s\n",
        "Job execution statistics:\n",
        " job count | % of all jobs | job time sum | time per job | job server\n",
        "         9 |        100.00 |       6.5400 |     0.726667 | local\n",
        "Time elapsed since server creation 38.5339999199\n",
        "\n"
       ]
      }
     ],
     "prompt_number": 5
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Sequential Code"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import math, sys, time\n",
      "import numpy as np\n",
      "import pp\n",
      "\n",
      "def isprime(n):\n",
      "    \"\"\"Returns True if n is prime and False otherwise\"\"\"\n",
      "    if not isinstance(n, int):\n",
      "        raise TypeError(\"argument passed to is_prime is not of 'int' type\")\n",
      "    if n < 2:\n",
      "        return False\n",
      "    if n == 2:\n",
      "        return True\n",
      "    max = int(math.ceil(math.sqrt(n)))\n",
      "    i = 2\n",
      "    while i <= max:\n",
      "        if n % i == 0:\n",
      "            return False\n",
      "        i += 1\n",
      "    return True\n",
      "\n",
      "def sum_primes(n):\n",
      "    \"\"\"Calculates sum of all primes below given integer n\"\"\"\n",
      "    return sum([x for x in xrange(2,n) if isprime(x)])\n",
      "\n",
      "# Submit a job of calulating sum_primes(100) for execution. \n",
      "# sum_primes - the function\n",
      "# (100,) - tuple with arguments for sum_primes\n",
      "# (isprime,) - tuple with functions on which function sum_primes depends\n",
      "# (\"math\",) - tuple with module names which must be imported before sum_primes execution\n",
      "# Execution starts as soon as one of the workers will become available\n",
      "result=sum_primes(100)\n",
      "\n",
      "# Retrieves the result calculated by job1\n",
      "# The value of job1() is the same as sum_primes(100)\n",
      "# If the job has not been finished yet, execution will wait here until result is available\n",
      "print \"Sum of primes below 100 is\", result\n",
      "\n",
      "start_time = time.time()\n",
      "\n",
      "# The following submits 8 jobs and then retrieves the results\n",
      "inputs = (100000, 100100, 100200, 100300, 100400, 100500, 100600, 100700)\n",
      "index=0\n",
      "jobs=0\n",
      "for input in inputs:\n",
      "    jobs = sum_primes(input)\n",
      "    print \"Sum of primes below\", input, \"is\", jobs\n",
      "print \"Time elapsed for sequential version: \", time.time() - start_time, \"s\"\n",
      "\n",
      "\n"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Sum of primes below 100 is 1060\n",
        "Sum of primes below"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 100000 is 454396537\n",
        "Sum of primes below"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 100100 is 454996777\n",
        "Sum of primes below"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 100200 is 455898156\n",
        "Sum of primes below"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 100300 is 456700218\n",
        "Sum of primes below"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 100400 is 457603451\n",
        "Sum of primes below"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 100500 is 458407033\n",
        "Sum of primes below"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 100600 is 459412387\n",
        "Sum of primes below"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 100700 is 460217613\n",
        "Time elapsed for sequential version:  3.41300010681 s\n"
       ]
      }
     ],
     "prompt_number": 7
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "#Quick start guide, clusters \n",
      "\n",
      "On the nodes \n",
      "\n",
      "1) Start parallel python execution server on all your remote computational nodes:\n",
      "\n",
      "    node-1> ./ppserver.py\n",
      "\n",
      "    node-2> ./ppserver.py\n",
      "\n",
      "    node-3> ./ppserver.py\n",
      "\n",
      "On the client \n",
      "\n",
      "2) Import pp module:\n",
      "\n",
      "    import pp\n",
      "\n",
      "3)  Create a list of all the nodes in your cluster (computers where you've run ppserver.py)\n",
      "\n",
      "    ppservers=(\"node-1\", \"node-2\", \"node-3\")\n",
      "\n",
      "4) Start pp execution server with the number of workers set to the number of processors in the system and list of ppservers to connect with :\n",
      "\n",
      "    job_server = pp.Server(ppservers=ppservers) \n",
      "\n",
      "5) Submit all the tasks for parallel execution:\n",
      "\n",
      "    f1 = job_server.submit(func1, args1, depfuncs1, modules1)\n",
      "\n",
      "    f2 = job_server.submit(func1, args2, depfuncs1, modules1)\n",
      "\n",
      "    f3 = job_server.submit(func2, args3, depfuncs2, modules2) \n",
      "   ...etc...\n",
      "\n",
      "6) Retrieve the results as needed:\n",
      "\n",
      "    r1 = f1()\n",
      "\n",
      "    r2 = f2()\n",
      "\n",
      "    r3 = f3() \n",
      "\n",
      "    ...etc...\n",
      "\n",
      " To find out how to achieve efficient parallelization with pp please take a look at examples\n",
      "\n",
      "  Quick start guide, clusters with autodiscovery\n",
      "On the nodes \n",
      "\n",
      "1) Start parallel python execution server on all your remote computational nodes:\n",
      "\n",
      "    node-1> ./ppserver.py -a\n",
      "\n",
      "    node-2> ./ppserver.py -a\n",
      "\n",
      "    node-3> ./ppserver.py -a\n",
      "\n",
      "On the client\n",
      "\n",
      "2) Import pp module:\n",
      "\n",
      "    import pp\n",
      "\n",
      "3)  Set ppservers list to auto-discovery:\n",
      "\n",
      "    ppservers=(\"*\",)\n",
      "\n",
      "4) Start pp execution server with the number of workers set to the number of processors in the system and list of ppservers to connect with :\n",
      "\n",
      "    job_server = pp.Server(ppservers=ppservers) \n",
      "\n",
      "5) Submit all the tasks for parallel execution:\n",
      "\n",
      "    f1 = job_server.submit(func1, args1, depfuncs1, modules1)\n",
      "\n",
      "    f2 = job_server.submit(func1, args2, depfuncs1, modules1)\n",
      "\n",
      "    f3 = job_server.submit(func2, args3, depfuncs2, modules2) \n",
      "   ...etc...\n",
      "\n",
      "6) Retrieve the results as needed:\n",
      "\n",
      "    r1 = f1()\n",
      "\n",
      "    r2 = f2()\n",
      "\n",
      "    r3 = f3() \n",
      "\n",
      "    ...etc...\n",
      "\n",
      " To find out how to achieve efficient parallelization with pp please take a look at examples "
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "#Advanced guide, clusters \n",
      "\n",
      "On the nodes  \n",
      "\n",
      "1) Start parallel python execution server on all your remote computational nodes (listen to a given port 35000,\n",
      "and local network interface only, accept only connections which know correct secret):\n",
      "\n",
      "    node-1> ./ppserver.py -p 35000 -i 192.168.0.101 -s \"mysecret\"\n",
      "\n",
      "    node-2> ./ppserver.py -p 35000 -i 192.168.0.102 -s \"mysecret\"\n",
      "\n",
      "    node-3> ./ppserver.py -p 35000 -i 192.168.0.103 -s \"mysecret\"\n",
      "\n",
      "On the client \n",
      "\n",
      "2) Import pp module:\n",
      "\n",
      "    import pp\n",
      "\n",
      "3)  Create a list of all the nodes in your cluster (computers where you've run ppserver.py)\n",
      "\n",
      "    ppservers=(\"node-1:35000\", \"node-2:35000\", \"node-3:35000\")\n",
      "\n",
      "4) Start pp execution server with the number of workers set to the number of processors in the system, \n",
      "list of ppservers to connect with and secret key to authorize the connection:\n",
      "\n",
      "    job_server = pp.Server(ppservers=ppservers, secret=\"mysecret\") \n",
      "\n",
      "5) Submit all the tasks for parallel execution:\n",
      "\n",
      "    f1 = job_server.submit(func1, args1, depfuncs1, modules1)\n",
      "\n",
      "    f2 = job_server.submit(func1, args2, depfuncs1, modules1)\n",
      "\n",
      "    f3 = job_server.submit(func2, args3, depfuncs2, modules2) \n",
      "   ...etc...\n",
      "\n",
      "6) Retrieve the results as needed:\n",
      "\n",
      "    r1 = f1()\n",
      "\n",
      "    r2 = f2()\n",
      "\n",
      "    r3 = f3() \n",
      "\n",
      "    ...etc...\n",
      "\n",
      " 7) Print the execution statistics:\n",
      "\n",
      "    job_server.print_stats()\n",
      "\n",
      "To find out how to achieve efficient parallelization with pp please take a look at examples\n"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "#Command line options, ppserver.py\n",
      "\n",
      "Usage: ppserver.py [-hda] [-i interface] [-b broadcast] [-p port] [-w nworkers] [-s secret] [-t seconds]\n",
      "\n",
      " Options:\n",
      "\n",
      "     -h                 : this help message\n",
      "\n",
      "     -d                 : debug\n",
      "\n",
      "     -a                 : enable auto-discovery service\n",
      "\n",
      "     -i interface       : interface to listen\n",
      "\n",
      "     -b broadcast       : broadcast address for auto-discovery service\n",
      "\n",
      "     -p port            : port to listen\n",
      "\n",
      "     -w nworkers        : number of workers to start\n",
      "\n",
      "     -s secret          : secret for authentication\n",
      "\n",
      "     -t seconds         : timeout to exit if no connections with clients exist\n",
      "\n",
      "     -k seconds         : socket timeout in seconds  "
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "This program demonstrates parallel version of quicksort algorithm using pp library"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#!/usr/bin/env python\n",
      "# Author: Vitalii Vanovschi\n",
      "# Desc: This program demonstrates parallel version of quicksort algorithm\n",
      "# implemented using pp module\n",
      "# Parallel Python Software: http://www.parallelpython.com\n",
      " \n",
      "import sys, random\n",
      "import pp\n",
      " \n",
      "def quicksort(a, n=-1, srv=None):\n",
      "    if len(a) <= 1:\n",
      "        return a\n",
      "    if n:\n",
      "        return quicksort([x for x in a if x < a[0]], n-1, srv) \\\n",
      "                + [a[0]] + quicksort([x for x in a[1:] if x >= a[0]], n-1, srv)\n",
      "    else:\n",
      "        return [srv.submit(quicksort, (a,))]\n",
      "    \n",
      " \n",
      "print \"\"\"Usage: python quicksort.py [ncpus]\n",
      "    [ncpus] - the number of workers to run in parallel, \n",
      "    if omitted it will be set to the number of processors in the system\n",
      "\"\"\"\n",
      " \n",
      "# tuple of all parallel python servers to connect with\n",
      "#ppservers = (\"*\",)\n",
      "#ppservers = (\"10.0.0.1\",)\n",
      "ppservers = ()\n",
      " \n",
      "\n",
      "# Creates jobserver with automatically detected number of workers\n",
      "job_server = pp.Server(ppservers=ppservers)\n",
      " \n",
      "print \"Starting pp with\", job_server.get_ncpus(), \"workers\"\n",
      " \n",
      "n = 1000000\n",
      "input = []\n",
      "for i in xrange(n):\n",
      "    input.append(random.randint(0,100000))\n",
      " \n",
      "# set n to a positive integer to create 2^n PP jobs \n",
      "# or to -1 to avoid using PP\n",
      " \n",
      "# 32 PP jobs\n",
      "n = 5\n",
      " \n",
      "# no PP\n",
      "#n = -1\n",
      " \n",
      "outputraw = quicksort(input, n, job_server)\n",
      " \n",
      "output = []\n",
      "for x in outputraw:\n",
      "    if callable(x):\n",
      "        output.extend(x())\n",
      "    else:\n",
      "        output.append(x)\n",
      " \n",
      "print \"first 30 numbers in increasing order:\", output[:30]\n",
      " \n",
      "job_server.print_stats()\n",
      " \n",
      "# Parallel Python Software: http://www.parallelpython.com\n"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Usage: python quicksort.py [ncpus]\n",
        "    [ncpus] - the number of workers to run in parallel, \n",
        "    if omitted it will be set to the number of processors in the system\n",
        "\n",
        "Starting pp with"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 4 workers\n",
        "first 30 numbers in increasing order:"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3]\n",
        "Job execution statistics:\n",
        " job count | % of all jobs | job time sum | time per job | job server\n",
        "        32 |        100.00 |      17.5210 |     0.547531 | local\n",
        "Time elapsed since server creation 8.90100002289\n",
        "\n"
       ]
      }
     ],
     "prompt_number": 9
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "This program computes the series $$1/1-1/2+1/3-1/4+1/5-1/6+...=ln(2)$$ in parallel using the library pp"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#!/usr/bin/python\n",
      "# File: dynamic_ncpus.py\n",
      "# Author: Vitalii Vanovschi\n",
      "# Desc: This program demonstrates parallel computations with pp module \n",
      "# and dynamic cpu allocation feature.\n",
      "# Program calculates the partial sum 1-1/2+1/3-1/4+1/5-1/6+... (in the limit it is ln(2))\n",
      "# Parallel Python Software: http://www.parallelpython.com\n",
      "\n",
      "import math, sys, md5, time\n",
      "import pp\n",
      "\n",
      "def part_sum(start, end):\n",
      "    \"\"\"Calculates partial sum\"\"\"\n",
      "    sum = 0\n",
      "    for x in xrange(start, end):\n",
      "        if x % 2 == 0:\n",
      "           sum -= 1.0 / x \n",
      "        else:\n",
      "           sum += 1.0 / x \n",
      "    return sum\n",
      "\n",
      "print \"\"\"Usage: python dynamic_ncpus.py\"\"\"\n",
      "print \n",
      "\n",
      "start = 1\n",
      "end = 20000000\n",
      "\n",
      "# Divide the task into 64 subtasks\n",
      "parts = 64\n",
      "step = (end - start) / parts + 1\n",
      "\n",
      "# Create jobserver\n",
      "job_server = pp.Server()\n",
      "\n",
      "# Execute the same task with different amount of active workers and measure the time\n",
      "for ncpus in (1, 2, 3, 4):\n",
      "    job_server.set_ncpus(ncpus)\n",
      "    jobs = []\n",
      "    start_time = time.time()\n",
      "    print \"Starting \", job_server.get_ncpus(), \" workers\"\n",
      "    for index in xrange(parts):\n",
      "        starti = start+index*step\n",
      "        endi = min(start+(index+1)*step, end)\n",
      "        # Submit a job which will calculate partial sum \n",
      "        # part_sum - the function\n",
      "        # (starti, endi) - tuple with arguments for part_sum\n",
      "        # () - tuple with functions on which function part_sum depends\n",
      "        # () - tuple with module names which must be imported before part_sum execution\n",
      "        jobs.append(job_server.submit(part_sum, (starti, endi)))\n",
      "   \n",
      "    # Retrieve all the results and calculate their sum\n",
      "    part_sum1 = sum([job() for job in jobs])\n",
      "    # Print the partial sum\n",
      "    print \"Partial sum is\", part_sum1, \"| diff =\", math.log(2) - part_sum1\n",
      "\n",
      "    print \"Time elapsed: \", time.time() - start_time, \"s\"\n",
      "    print\n",
      "job_server.print_stats()\n",
      "\n",
      "# Parallel Python Software: http://www.parallelpython.com\n"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Usage: python dynamic_ncpus.py\n",
        "\n",
        "Starting "
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 1  workers\n",
        "Partial sum is"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 0.69314720556 | diff = -2.50000427027e-08\n",
        "Time elapsed:  5.85299992561 s\n",
        "\n",
        "Starting  2  workers\n",
        "Partial sum is"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 0.69314720556 | diff = -2.50000427027e-08\n",
        "Time elapsed:  3.3180000782 s\n",
        "\n",
        "Starting  3  workers\n",
        "Partial sum is"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 0.69314720556 | diff = -2.50000427027e-08\n",
        "Time elapsed:  2.82099986076 s\n",
        "\n",
        "Starting  4  workers\n",
        "Partial sum is"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        " 0.69314720556 | diff = -2.50000427027e-08\n",
        "Time elapsed:  2.74000000954 s\n",
        "\n",
        "Job execution statistics:\n",
        " job count | % of all jobs | job time sum | time per job | job server\n",
        "       256 |        100.00 |      31.2170 |     0.121941 | local\n",
        "Time elapsed since server creation 14.7329998016\n",
        "\n"
       ]
      }
     ],
     "prompt_number": 12
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import math\n",
      "math.log(2)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "pyout",
       "prompt_number": 6,
       "text": [
        "0.6931471805599453"
       ]
      }
     ],
     "prompt_number": 6
    }
   ],
   "metadata": {}
  }
 ]
}