{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "CPU_CORES # -> system var that contains the numbr of available cores" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nprocs() # -> the currently available, to julia, number of cores" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "3-element Array{Any,1}:\n", " 2\n", " 3\n", " 4" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "addprocs(CPU_CORES - 1) # -> Make all cores available to Julia" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "RemoteRef(2,1,9)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r = remotecall(2, rand, 2, 2) # -> we can make a call, on processor 2, to the function rand with arguments 2 and 2." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2x2 Array{Float64,2}:\n", " 0.195311 0.699111\n", " 0.763146 0.517326" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fetch(r) # -> this function allows us to fetch the data that was created on another processor and \n", "#bring it into the scope of the master processor" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# can you create and fetch a matrix of zeros of size 14 by 14 on processor 3?\n", "\n", "\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2x2 Array{Float64,2}:\n", " 0.984253 0.618944\n", " 0.481985 0.472362" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s = @spawnat 2 rand(2,2) # -> a much nicer syntax that makes use of a julia macro\n", "fetch(s)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2x2 Array{Float64,2}:\n", " 0.0885738 0.373951\n", " 0.147095 0.279836" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "k = @fetchfrom 2 rand(2,2)# -> spawn on processor 2 and then fetch on completion\n", "k" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "myid() # -> function for determining the process ID" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@fetchfrom 3 myid() # -> this makes the macro behaviour easier to see. The function was called on \n", "#processor 3 and so returned ID 3" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r = @spawn myid() # -> @spawn chooses an available processor to run the function on this will change on each run\n", "fetch(r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In order to see the parrallel abilities of Julia we shall look at an embarrassingly parrallel problem - the calculation of $\\pi$ via Monte Carlo sampling. We will randomly draw x and y samples in the range [-1,1] and calculate the distance from the origin. If the distance is less than one then the sample lies within the circle if not then it lies outside. By calculating the ratio of interior to exterior points we can estimate $\\pi$ - the more samples we take the better our approximation. The fact that each sample is independent of all others makes this an excellent candidate for parallelism. " ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "compute_pi (generic function with 1 method)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function compute_pi(N::Int)\n", " \"\"\"\n", " Compute pi with a Monte Carlo simulation of N darts thrown in [-1,1]^2\n", " Returns estimate of pi\n", " \"\"\"\n", " n_landed_in_circle = 0 # counts number of points that have radial coordinate < 1, i.e. in circle\n", " for i = 1:N\n", " x = rand() * 2 - 1 # uniformly distributed number on x-axis\n", " y = rand() * 2 - 1 # uniformly distributed number on y-axis\n", " \n", " r2 = x*x + y*y # radius squared, in radial coordinates\n", " if r2 < 1.0\n", " n_landed_in_circle += 1\n", " end\n", " end\n", " \n", " return n_landed_in_circle / N * 4.0 \n", "end" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "3.14151268" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pi = compute_pi(int(10e7)) # -> we can compute an approximation for pi for N = 10e7" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "100-element Array{Float64,1}:\n", " 3.6 \n", " 2.90909\n", " 2.46154\n", " 2.93333\n", " 3.05882\n", " 3.4 \n", " 2.78261\n", " 2.37037\n", " 2.96774\n", " 2.74286\n", " 3.6 \n", " 3.13043\n", " 3.09434\n", " ⋮ \n", " 3.14048\n", " 3.14229\n", " 3.14186\n", " 3.14015\n", " 3.14086\n", " 3.14058\n", " 3.14252\n", " 3.14159\n", " 3.14152\n", " 3.14218\n", " 3.14159\n", " 3.14254" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pis = map(compute_pi,int(logspace(1,7,100))) # -> we can use the map function to calculate a range of approximations for\n", "# different values of N" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAqwAAAIYCAYAAAComb5TAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAIABJREFUeJzs3Xl4VPXZ//HPZAGSEPbIYhQUEYS6FLBuVRGeoijgowgBlWqtoFWroo+PtYut1latVVyoCkqxKGIRrRVK1R+tVn2KVqFqRSMQWUQWIUEgBLKe3x+3x2yznMlsZ5L367rmGjhzzpwzS5LP3HN/vyfgOI4jAAAAwKcyUn0AAAAAQDgEVgAAAPgagRUAAAC+RmAFAACArxFYAQAA4GsEVgAAAPgagRUAAAC+RmAFAACArxFYAQAA4GsEVgAAAPia58C6evVqTZw4Uf3791deXp4KCgp0+umna+nSpZ62X7lypcaOHavevXsrPz9fxx57rB566CHV1dW1+OABAADQ+mV5XXHTpk0qLy/XpZdeqj59+qiiokKLFy/W+PHjNXv2bE2bNi3ktitXrtTJJ5+sgQMH6kc/+pFyc3O1bNkyXXfddSopKdH9998flwcDAACA1ifgOI7T0o3r6uo0bNgwHThwQB9//HHI9aZPn64nn3xSW7duVZcuXb5ePmLECL333nv68ssvW3oIAAAAaOVi6mHNyMhQYWGhdu/eHXa9PXv2qH379urcuXOj5b169VJubm4shwAAAIBWLurAWlFRoZ07d6qkpEQzZ87USy+9pFGjRoXd5owzztCePXt0xRVXqLi4WBs3btSjjz6qP/3pT7rllltafPAAAABo/aJuCbjyyis1Z84cSVZhnTBhgubMmdOsetpQXV2drr/+es2ePVvV1dWSpMzMTP3ud7/T9OnTYzh8AAAAtHaeB125ZsyYoUmTJunzzz/XokWLVFNTo8rKyrDbZGRk6PDDD9dZZ52liRMnqkOHDnr66ad1zTXXqGfPnjr33HNb/AAAAADQusU06EqSzjzzTH355Zd6++23Q65z11136cEHH9S6desa9ayOHDlSa9as0caNG5WZmdlsu507d+rll19Wv379lJOTE8thAgAAIAH279+vDRs26Mwzz1SPHj0SsxMnRrNnz3YCgYCzZs2akOsccsghzsUXX9xs+X333ecEAgGnpKQk6HZPPfWUI4kLFy5cuHDhwoWLzy9PPfVUrLEypKhbAprav3+/JIWdKeCLL75QbW1ts+VuP2tN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rCKwAgAAJFZaB9b8fLtuGliT0RIQCIQ/eQDTWgEAAMRHWgfWzEz76r9paExGS4AUObBSYQUAAIid58D6zjvv6JprrtGQIUPUsWNH9e3bV0VFRVq7dq3nnS1fvlwjR45Uly5d1KlTJw0fPlyLFi1q0YG7OndOTYXV3TeBFQAAILGyvK549913a8WKFZo4caKOOeYYbd26VbNmzdLQoUP11ltvaciQIWG3nzdvni6//HKNHj1ad955pzIzM1VcXKzNmzfH9ACanp61qsouyQqsoU4cwLRWAAAA8eE5sN544406/vjjlZVVv0lRUZGOPvpo3XXXXXryySdDbrthwwZdffXVuvbaazVz5szYjriJTp0aVzn37bNrWgIAAABaB88tASeddFKjsCpJRxxxhAYPHqzi4uKw2z766KNyHEe33367JKm8vFyO47TgcJtrWuV0A2syKqxNw3JDBFYAAID4iGnQleM42r59u3r06BF2veXLl2vQoEFaunSpCgsL1alTJ/Xo0UO33nprzMG1aUtAebldU2EFAABoHWIKrAsWLNCWLVtUVFQUdr21a9dq06ZNuuyyy3T55Zfrueee05gxY3THHXfoJz/5SSyH0CywJrPCyrRWAAAAiee5h7Wp4uJiXX311Tr55JN1ySWXhF3XbQG4++67ddNNN0mSzjvvPJWVlemBBx7Qj3/8Y3VsYcJsGhrdCqsfAisVVgAAgNi1qMK6bds2nXPOOeratasWL16sQCAQdv2cnBwFAgFNmTKl0fLJkydr//79eu+991pyGJJS3xKwd69UV9d4ueMQWAEAAOIl6grr7t27NWbMGO3Zs0dvvPGGevXqFXGbPn36qKSkRD179my0/KCDDpIk7dq1K+z2M2bMUOfOnRstmzJliqZMmZLylgDHsdDa8PCqquyawAoAAFqThQsXauHChY2W7Q71dXMcRRVYDxw4oHHjxmndunVfD6TyYvjw4Vq3bp02b96sww477OvlW7ZskSQVFBSE3X7mzJkaOnRo0Ns6daqvcmZkJL/CKllbQMPAun+/XRNYAQBAa+IWDBtatWqVhg0bltD9em4JqK2tVVFRkd5++209++yzOuGEE4Kut23bNhUXF6umpubrZe6grLlz5369rK6uTvPmzVP37t1jepBuldMNquXlNtgpM7PFdxnVvqXmfawEVgAA/n979x9bVX3/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", "text/plain": [ "Figure(PyObject )" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "1-element Array{Any,1}:\n", " PyObject " ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using PyPlot # -> import library which allows us to use matplotlibs plotting functions\n", "semilogx(int(logspace(1,7,100)),pis) # -> logplot of pi approximations" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "exception on 4: exception on exception on 2: 3: ERROR: function compute_pi not defined on process 4\n", " in error at error.jl:21\n", " in anonymous at serialize.jl:397\n", " in anonymous at multi.jl:855\n", " in run_work_thunk at multi.jl:621\n", " in anonymous at task.jl:855\n", "ERROR: function compute_pi not defined on process 2\n", " in error at error.jl:21\n", " in anonymous at serialize.jl:397\n", " in anonymous at multi.jl:855\n", " in run_work_thunk at multi.jl:621\n", " in anonymous at task.jl:855\n", "ERROR: function compute_pi not defined on process 3\n", " in error at error.jl:21\n", " in anonymous at serialize.jl:397\n", " in anonymous at multi.jl:855\n", " in run_work_thunk at multi.jl:621\n", " in anonymous at task.jl:855\n" ] }, { "data": { "text/plain": [ "3-element Array{Any,1}:\n", " ErrorException(\"function compute_pi not defined on process 2\")\n", " ErrorException(\"function compute_pi not defined on process 3\")\n", " ErrorException(\"function compute_pi not defined on process 4\")" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pis = pmap(compute_pi,int(logspace(1,8,100))) # -> pmap function applies the function to each of the arguments making\n", "# use of processors as they come available" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Only the master process knows about this function definition and so when pmap asks other proceeses to use this function they have no idea what to do! We must use the julia @everywhere macro to let all processes know about a function. " ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": true }, "outputs": [], "source": [ "@everywhere function compute_pi(N::Int)\n", " \"\"\"\n", " Compute pi with a Monte Carlo simulation of N darts thrown in [-1,1]^2\n", " Returns estimate of pi\n", " \"\"\"\n", " n_landed_in_circle = 0 # counts number of points that have radial coordinate < 1, i.e. in circle\n", " for i = 1:N\n", " x = rand() * 2 - 1 # uniformly distributed number on x-axis\n", " y = rand() * 2 - 1 # uniformly distributed number on y-axis\n", " \n", " r2 = x*x + y*y # radius squared, in radial coordinates\n", " if r2 < 1.0\n", " n_landed_in_circle += 1\n", " end\n", " end\n", " \n", " return n_landed_in_circle / N * 4.0 \n", "end" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "100-element Array{Any,1}:\n", " 4.0 \n", " 2.66667\n", " 3.14286\n", " 3.25 \n", " 2.73684\n", " 2.6087 \n", " 2.51852\n", " 3.22581\n", " 2.91892\n", " 2.51163\n", " 2.98039\n", " 2.93333\n", " 3.49296\n", " ⋮ \n", " 3.14252\n", " 3.14111\n", " 3.14164\n", " 3.14141\n", " 3.14135\n", " 3.14147\n", " 3.14203\n", " 3.14137\n", " 3.14153\n", " 3.14173\n", " 3.14159\n", " 3.14147" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pis = pmap(compute_pi,int(logspace(1,8,100)))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Can you write some code that will allow you to time the average execution of the normal map function and the \n", "# pmap function? \n", "#\n", "# HINT: tic() and tock()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "-0.11710952409816672" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# we can perform reduction loops in parrallel using @parallel\n", "sum = @parallel (+) for i = 1:1000000\n", " sin(i)\n", "end" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [], "source": [ "@everywhere function par_compute_pi(N,ncores)\n", " each = int(floor(N/ncores))\n", " left = N%ncores\n", "\n", " sum_of_pis = @parallel (+) for i=1:ncores\n", " if i <= left\n", " compute_pi(each+1)\n", " else\n", " compute_pi(each)\n", " end\n", " end\n", "\n", " return sum_of_pis / ncores\n", "end" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "elapsed time: 0.135106279 seconds\n", "elapsed time: 0.122012 seconds\n", "elapsed time: 0.122494676 seconds\n", "elapsed time: 0.124619668 seconds\n", "elapsed time: 0.121832826 seconds\n", "elapsed time: 0.124469028 seconds\n", "elapsed time: 0.130674546 seconds\n", "elapsed time: 0.12665145 seconds\n", "elapsed time: 0.129598744 seconds\n", "elapsed time: 0.120853728 seconds\n", "avg time = 0.1258312945\n" ] } ], "source": [ "time = 0 \n", "for i = 1:10\n", " tic() \n", " pi = par_compute_pi(int(10e7),1)\n", " time+=toc()\n", "end\n", "\n", "println(\"avg time = \",time/10)" ] } ], "metadata": { "kernelspec": { "display_name": "Julia 0.3.7", "language": "julia", "name": "julia 0.3" }, "language_info": { "name": "julia", "version": "0.3.7" } }, "nbformat": 4, "nbformat_minor": 0 }