{ "cells": [ { "cell_type": "markdown", "id": "f785959f-8601-4d0c-9542-0478b3776622", "metadata": {}, "source": [ "Via sockets, programs communicate with each other. Use of the multiprocessing module is illustrated." ] }, { "cell_type": "markdown", "id": "665a86d7-a070-41cc-8e0d-27a90fec3953", "metadata": {}, "source": [ "All code in this notebook is for viewing only as it should be executed in a terminal window." ] }, { "cell_type": "markdown", "id": "c9758698-4e36-4238-b98e-29d25db261c4", "metadata": {}, "source": [ "# 1. Client/Server Computing to Estimate $\\pi$" ] }, { "cell_type": "markdown", "id": "dabedb7d-9a0d-422b-a8c3-e63b10b7307b", "metadata": {}, "source": [ "The code below implements a server for Monte Carlo method to estimate pi,\n", "with two clients. The server dispatches the seeds and collects results.\n", "The code should be executed in a terminal window and is included below for viewing only." ] }, { "cell_type": "markdown", "id": "127def0c-f7f7-41be-b8f3-975a6f175608", "metadata": {}, "source": [ "```\n", "from socket import socket as Socket\n", "from socket import AF_INET, SOCK_STREAM\n", "\n", "HOSTNAME = '' # blank so any address can be used\n", "NUMBER = 11267 # number for the port\n", "BUFFER = 80 # size of the buffer\n", "\n", "SERVER_ADDRESS = (HOSTNAME, NUMBER)\n", "SERVER = Socket(AF_INET, SOCK_STREAM)\n", "SERVER.bind(SERVER_ADDRESS)\n", "SERVER.listen(2)\n", "\n", "print('server waits for connections...')\n", "\n", "FIRST, FIRST_ADDRESS = SERVER.accept()\n", "SECOND, SECOND_ADDRESS = SERVER.accept()\n", "\n", "FIRST.send('1'.encode())\n", "SECOND.send('2'.encode())\n", "\n", "print('server waits for results...')\n", "\n", "PART1 = FIRST.recv(BUFFER).decode()\n", "PART2 = SECOND.recv(BUFFER).decode()\n", "\n", "RESULT = 2*(float(PART1)+float(PART2))\n", "print('approximation for pi =', RESULT)\n", "\n", "SERVER.close()\n", "```" ] }, { "cell_type": "markdown", "id": "1633aca6-199c-4ff9-b795-45d7c6e53530", "metadata": {}, "source": [ "The clients connecting to the server are *compute clients* or *workers*. The code for the client script is included below." ] }, { "cell_type": "markdown", "id": "209adbb2-9269-4251-987b-5a5503a7de7c", "metadata": {}, "source": [ "```\n", "from random import seed, uniform\n", "from socket import socket as Socket\n", "from socket import AF_INET, SOCK_STREAM\n", "\n", "HOSTNAME = 'localhost' # on same host\n", "NUMBER = 11267 # same port number\n", "BUFFER = 80 # size of the buffer\n", "\n", "SERVER_ADDRESS = (HOSTNAME, NUMBER)\n", "CLIENT = Socket(AF_INET, SOCK_STREAM)\n", "CLIENT.connect(SERVER_ADDRESS)\n", "\n", "print('client is connected')\n", "DATA = CLIENT.recv(BUFFER).decode()\n", "print('client received %s' % DATA)\n", "\n", "seed(int(DATA))\n", "\n", "N = 10**7\n", "CNT = 0\n", "for i in range(0, N):\n", " xpt = uniform(0, 1)\n", " ypt = uniform(0, 1)\n", " if xpt**2 + ypt**2 <= 1:\n", " CNT = CNT + 1\n", "R = float(CNT)/N\n", "print('client computes %.12f' % R)\n", "\n", "CLIENT.send(str(R).encode())\n", "\n", "CLIENT.close()\n", "```" ] }, { "cell_type": "markdown", "id": "6d22d6e4-76d6-4df6-be15-32b4b06e3522", "metadata": {}, "source": [ "The code in this section is to illustrate the sockets, but for practical purposes, this way of working is rather tedious. Therefore, the `multiprocessing` module is introduced next." ] }, { "cell_type": "markdown", "id": "c8a7ce83-4811-4fc4-b4e7-dbdc0678718d", "metadata": {}, "source": [ "# 2. Multiprocessing" ] }, { "cell_type": "markdown", "id": "39a7c481-d783-4f5a-b26a-c1db313b0f33", "metadata": {}, "source": [ "The code below should be executed in a terminal window. It is included for viewing only." ] }, { "cell_type": "markdown", "id": "20523bbd-7bc5-4dad-9814-e0534f219ab9", "metadata": {}, "source": [ "```\n", "from multiprocessing import Process\n", "from time import sleep\n", "\n", "def say_hello(name, tslp):\n", " \"\"\"\n", " Process with name says hello,\n", " prints its process id, and\n", " sleeps for tslp seconds.\n", " \"\"\"\n", " from os import getpid\n", " prt = 'hello from ' + name \\\n", " + ' with process id ' + str(getpid())\n", " print(prt)\n", " print(name, 'sleeps', tslp, 'seconds')\n", " sleep(tslp)\n", " print(name, 'wakes up')\n", "\n", "def main():\n", " \"\"\"\n", " Defines two processes.\n", " \"\"\"\n", " from os import getpid\n", " print('the process id of main is', getpid())\n", " apr = Process(target=say_hello, args = ('A', 4))\n", " bpr = Process(target=say_hello, args = ('B', 1))\n", " print('starting two processes...')\n", " apr.start()\n", " sleep(1) # to make printing look nice\n", " bpr.start()\n", " print('waiting for processes to wake up...')\n", " apr.join()\n", " bpr.join()\n", " print('processes are done')\n", "```" ] }, { "cell_type": "markdown", "id": "8d6dbaff-450a-434c-9fbd-b654113d982f", "metadata": {}, "source": [ "# 3. Multiprocessing to Estimate $\\pi$" ] }, { "cell_type": "markdown", "id": "a0f6a9b9-8008-4082-8655-e20960fa93d2", "metadata": {}, "source": [ "The main advantage of using the `multiprocessing` module over the explicit client/server communication is that one single program defines all computations." ] }, { "cell_type": "markdown", "id": "c1d0b218-7fa7-46ba-971a-786665ffee3e", "metadata": {}, "source": [ "```\n", "from multiprocessing import Process, Queue\n", "from multiprocessing import freeze_support\n", "from math import pi\n", "from random import uniform as u\n", "from random import seed\n", "\n", "def monte_carlo4pi(nbr, nsd, result):\n", " \"\"\"\n", " Estimates pi with nbr samples,\n", " using nsd as seed.\n", " Adds the result to the queue q.\n", " \"\"\"\n", " seed(nsd)\n", " cnt = 0\n", " for _ in range(nbr):\n", " (x, y) = (u(-1, 1), u(-1, 1))\n", " if x**2 + y**2 <= 1:\n", " cnt = cnt + 1\n", " result.put(cnt)\n", "\n", "def main():\n", " \"\"\"\n", " Prompts the user for the number of samples\n", " and the number of processes.\n", " \"\"\"\n", " nbr = int(input('Give the number of samples : '))\n", " npr = int(input('Give the number of processes : '))\n", " queues = [Queue() for _ in range(npr)]\n", " procs = []\n", " for k in range(1, npr+1):\n", " procs.append(Process(target=monte_carlo4pi, \\\n", " args=(nbr, k, queues[k-1])))\n", " for process in procs:\n", " process.start()\n", " for process in procs:\n", " process.join()\n", " app = 4*sum([q.get()/nbr for q in queues])/npr\n", " print(app, f\"error : {abs(app - pi):.3e}\")\n", "\n", "if __name__ == '__main__':\n", " freeze_support()\n", " main()\n", "```" ] }, { "cell_type": "markdown", "id": "a0025037-f9a8-4c2b-adc6-0cb7e3be8ce7", "metadata": {}, "source": [ "The `freeze_support()` is critical to make the code run on Windows computers." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.10" } }, "nbformat": 4, "nbformat_minor": 5 }