{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Multiprocessing Module\n", "\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/borchero/pyblaze/blob/master/docs/examples/multiprocessing.ipynb)\n", "[![Download Jupyter Notebbok](https://img.shields.io/badge/Github-Download-brightgreen)](https://github.com/borchero/pyblaze/blob/master/docs/examples/multiprocessing.ipynb)\n", "\n", "When working with independent data, spreading computations across multiple cores often provides an easy way to (linearly) increase a program’s throughput.\n", "\n", "One of the most common forms of parallelism is to split loops across multiple threads. Unfortunately, Python does not support such parallelism easily, especially if computations are CPU-bound. Using simple threads is often not an option due to the GIL and working with processes is often tedious. This is particularly true when working with PyTorch tensors as they have to be passed between processes over queues.\n", "\n", "The `multiprocessing` module of PyBlaze aims to make it possible to speed up computations easily, providing a high-level interface." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Vectorization\n", "\n", "PyBlaze refers to vectorization as the process of parallelizing for-loops of the following form:\n", "\n", "```python\n", "result = []\n", "for item in iterable:\n", " result.append(map(item))\n", "```\n", "\n", "PyBlaze’s class providing this functionality is the `Vectorizer` class in the `multiprocessing` module. In the background, the vectorizer handles everything such as creating processes, ensuring their shutdown, passing items and results between processes. Due to the class’s simplicity, it can often be used as a drop-in replacement for existing for-loops which not only reduces runtime but enhances readability." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example Program\n", "\n", "Consider, for example, an array `text` of strings which you want to tokenize according to a complex function `tokenize`. The function takes as input a single string and returns its tokenization." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import time\n", "\n", "texts = [\n", " ''.join(np.random.choice(['a', 'b', 'c', 'd', 'e', ' '], size=(20,)))\n", " for _ in range(100)\n", "]\n", "\n", "def tokenize(text):\n", " time.sleep(0.01)\n", " return text.split()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The sequential implementation is very easy, however, not particularly efficient:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def sequential(texts):\n", " return [tokenize(t) for t in texts]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `Vectorizer` can easily be used to compute the tokenizations of all texts in parallel:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import pyblaze.multiprocessing as xmp\n", "\n", "def parallel(texts):\n", " tokenizer = xmp.Vectorizer(tokenize, num_workers=4)\n", " return tokenizer.process(texts)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now compare the runtime of the sequential and vectorized implementation:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.01 s ± 86.3 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], "source": [ "%timeit sequential(texts)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "280 ms ± 1.17 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], "source": [ "%timeit parallel(texts)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, the vectorized implementation achieves an almost linear speedup by distributing work across processes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Advanced Features" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `Vectorizer` class also provides some more advanced features, such as initializing workers. Consult the class’s docs for more information on that." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.7.6" } }, "nbformat": 4, "nbformat_minor": 4 }