[![tests](https://github.com/alex-petrenko/faster-fifo/actions/workflows/test-ci.yml/badge.svg)](https://github.com/alex-petrenko/faster-fifo/actions/workflows/test-ci.yml)
[![Downloads](https://pepy.tech/badge/faster-fifo)](https://pepy.tech/project/faster-fifo)
[<img src="https://img.shields.io/discord/987232982798598164?label=discord">](https://discord.gg/d9VZmMWejh)
<!-- [![codecov](https://codecov.io/gh/alex-petrenko/faster-fifo/branch/master/graph/badge.svg)](https://codecov.io/gh/alex-petrenko/faster-fifo) -->
# faster-fifo
Faster alternative to Python's standard multiprocessing.Queue (IPC FIFO queue). Up to 30x faster in some configurations.
Implemented in C++ using POSIX mutexes with PTHREAD_PROCESS_SHARED attribute. Based on a circular buffer, low footprint, brokerless.
Completely mimics the interface of the standard multiprocessing.Queue, so can be used as a drop-in replacement.
Adds `get_many()` and `put_many()` methods to receive/send multiple messages at once for the price of a single lock.
## Requirements
- Linux or MacOS
- Python 3.6 or newer
- GCC 4.9.0 or newer
## Installation
```pip install faster-fifo```
(on a fresh Linux installation you might need some basic compiling tools `sudo apt install --reinstall build-essential gcc g++`)
## Manual build instructions
```
pip install Cython
python setup.py build_ext --inplace
pip install -e .
```
## Usage example
```Python
from faster_fifo import Queue
from queue import Full, Empty
q = Queue(1000 * 1000) # specify the size of the circular buffer in the ctor
# any pickle-able Python object can be added to the queue
py_obj = dict(a=42, b=33, c=(1, 2, 3), d=[1, 2, 3], e='123', f=b'kkk')
q.put(py_obj)
assert q.qsize() == 1
retrieved = q.get()
assert q.empty()
assert py_obj == retrieved
for i in range(100):
try:
q.put(py_obj, timeout=0.1)
except Full:
log.debug('Queue is full!')
num_received = 0
while num_received < 100:
# get multiple messages at once, returns a list of messages for better performance in many-to-few scenarios
# get_many does not guarantee that all max_messages_to_get will be received on the first call, in fact
# no such guarantee can be made in multiprocessing systems.
# get_many() will retrieve as many messages as there are available AND can fit in the pre-allocated memory
# buffer. The size of the buffer is increased gradually to match demand.
messages = q.get_many(max_messages_to_get=100)
num_received += len(messages)
try:
q.get(timeout=0.1)
assert True, 'This won\'t be called'
except Empty:
log.debug('Queue is empty')
```
## Performance comparison (faster-fifo vs multiprocessing.Queue)
##### System #1 (Intel(R) Core(TM) i9-7900X CPU @ 3.30GHz, 10 cores, Ubuntu 18.04)
*(measured execution times in seconds)*
| | multiprocessing.Queue | faster-fifo, get() | faster-fifo, get_many() |
|---------------------------------------------------|:---------------------:|:-----------------------:|:-------------------------:|
| 1 producer 1 consumer (200K msgs per producer) | 2.54 | 0.86 | 0.92 |
| 1 producer 10 consumers (200K msgs per producer) | 4.00 | 1.39 | 1.36 |
| 10 producers 1 consumer (100K msgs per producer) | 13.19 | 6.74 | 0.94 |
| 3 producers 20 consumers (100K msgs per producer) | 9.30 | 2.22 | 2.17 |
| 20 producers 3 consumers (50K msgs per producer) | 18.62 | 7.41 | 0.64 |
| 20 producers 20 consumers (50K msgs per producer) | 36.51 | 1.32 | 3.79 |
##### System #2 (Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz, 2 cores, Ubuntu 18.04)
*(measured execution times in seconds)*
| | multiprocessing.Queue | faster-fifo, get() | faster-fifo, get_many() |
|---------------------------------------------------|:---------------------:|:-----------------------:|:-------------------------:|
| 1 producer 1 consumer (200K msgs per producer) | 7.86 | 2.09 | 2.2 |
| 1 producer 10 consumers (200K msgs per producer) | 11.68 | 4.01 | 3.88 |
| 10 producers 1 consumer (100K msgs per producer) | 44.48 | 16.68 | 5.98 |
| 3 producers 20 consumers (100K msgs per producer) | 22.59 | 7.83 | 7.49 |
| 20 producers 3 consumers (50K msgs per producer) | 66.3 | 22.3 | 6.35 |
| 20 producers 20 consumers (50K msgs per producer) | 78.75 | 14.39 | 15.78 |
## Run tests
`python -m unittest`
(there are also C++ unit tests, should run them if C++ code was altered)
## Recent PyPI releases
##### v1.4.6
* Added missing `<cstdio>` causing issues with newer g++. Thank you [mesaglio](https://github.com/mesaglio)!
##### v1.4.5
* Added method `data_size()` to query the total size of the messages in queue (in bytes). Thank you [@LucaNicosia](https://github.com/LucaNicosia)!
##### v1.4.4
* Fixed an obscure issue with the TLSBuffer ctor being called without arguments (guessing it's Cython's weirdness)
##### v1.4.3
* Simplified usage with "spawn" multiprocessing context. No need to use `faster_fifo_reduction` anymore. Thank you [@MosBas](https://github.com/MosBas)!
##### v1.4.2
* Fixed an issue with the custom Queue pickler
##### v1.4.1
* Fixed multithreading issues using threading.local for message recv buffer (huge thanks to [@brianmacy](https://github.com/brianmacy)!)
* Better error reporting in Cython and C++
* Added threading tests
##### v1.4.0
* Increase default receive buffer size from 10 bytes to 5000 bytes.
##### v1.3.1
* Minor change: better debugging messages + improved C++ tests
##### v1.3.0
* Now support custom serializers and deserializers instead of Pickle (thank you [@beasteers](https://github.com/beasteers)!):
```Python
q = Queue(max_size_bytes=100000, loads=custom_deserializer, dumps=custom_serializer)
```
## Footnote
Originally designed for SampleFactory, a high-throughput asynchronous RL codebase https://github.com/alex-petrenko/sample-factory.
Programmed by [Aleksei Petrenko](https://alex-petrenko.github.io/) and Tushar Kumar at [USC RESL](https://robotics.usc.edu/resl/people/).
Developed under MIT License, feel free to use for any purpose, commercial or not, at your own risk.
If you wish to cite this repository:
```
@misc{faster-fifo,
author={Petrenko, Aleksei and Kumar, Tushar},
title={A Faster Alternative to Python's multiprocessing.Queue},
publisher={GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/alex-petrenko/faster-fifo}},
year={2020},
}
```
Raw data
{
"_id": null,
"home_page": "https://github.com/alex-petrenko/faster-fifo",
"name": "faster-fifo",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": null,
"keywords": "multiprocessing data structures",
"author": "Aleksei Petrenko & Tushar Kumar",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/fa/f2/6f36b83383d0f2f20ffc5af696177e70c7aaa0b713d291e6817b4522371d/faster_fifo-1.4.6.tar.gz",
"platform": null,
"description": "[![tests](https://github.com/alex-petrenko/faster-fifo/actions/workflows/test-ci.yml/badge.svg)](https://github.com/alex-petrenko/faster-fifo/actions/workflows/test-ci.yml)\n[![Downloads](https://pepy.tech/badge/faster-fifo)](https://pepy.tech/project/faster-fifo)\n[<img src=\"https://img.shields.io/discord/987232982798598164?label=discord\">](https://discord.gg/d9VZmMWejh)\n\n<!-- [![codecov](https://codecov.io/gh/alex-petrenko/faster-fifo/branch/master/graph/badge.svg)](https://codecov.io/gh/alex-petrenko/faster-fifo) -->\n\n# faster-fifo\n\nFaster alternative to Python's standard multiprocessing.Queue (IPC FIFO queue). Up to 30x faster in some configurations.\n\nImplemented in C++ using POSIX mutexes with PTHREAD_PROCESS_SHARED attribute. Based on a circular buffer, low footprint, brokerless.\nCompletely mimics the interface of the standard multiprocessing.Queue, so can be used as a drop-in replacement.\n\nAdds `get_many()` and `put_many()` methods to receive/send multiple messages at once for the price of a single lock.\n\n## Requirements \n\n- Linux or MacOS\n- Python 3.6 or newer\n- GCC 4.9.0 or newer\n\n## Installation\n\n```pip install faster-fifo```\n\n(on a fresh Linux installation you might need some basic compiling tools `sudo apt install --reinstall build-essential gcc g++`)\n\n## Manual build instructions\n\n```\npip install Cython\npython setup.py build_ext --inplace\npip install -e .\n```\n\n## Usage example\n\n```Python\nfrom faster_fifo import Queue\nfrom queue import Full, Empty\n\nq = Queue(1000 * 1000) # specify the size of the circular buffer in the ctor\n\n# any pickle-able Python object can be added to the queue\npy_obj = dict(a=42, b=33, c=(1, 2, 3), d=[1, 2, 3], e='123', f=b'kkk')\nq.put(py_obj)\nassert q.qsize() == 1\n\nretrieved = q.get()\nassert q.empty()\nassert py_obj == retrieved\n\nfor i in range(100):\n try:\n q.put(py_obj, timeout=0.1)\n except Full:\n log.debug('Queue is full!')\n\nnum_received = 0\nwhile num_received < 100:\n # get multiple messages at once, returns a list of messages for better performance in many-to-few scenarios\n # get_many does not guarantee that all max_messages_to_get will be received on the first call, in fact\n # no such guarantee can be made in multiprocessing systems.\n # get_many() will retrieve as many messages as there are available AND can fit in the pre-allocated memory\n # buffer. The size of the buffer is increased gradually to match demand.\n messages = q.get_many(max_messages_to_get=100)\n num_received += len(messages)\n\ntry:\n q.get(timeout=0.1)\n assert True, 'This won\\'t be called'\nexcept Empty:\n log.debug('Queue is empty')\n\n```\n\n## Performance comparison (faster-fifo vs multiprocessing.Queue)\n\n##### System #1 (Intel(R) Core(TM) i9-7900X CPU @ 3.30GHz, 10 cores, Ubuntu 18.04)\n\n*(measured execution times in seconds)*\n\n| | multiprocessing.Queue | faster-fifo, get() | faster-fifo, get_many() |\n|---------------------------------------------------|:---------------------:|:-----------------------:|:-------------------------:|\n| 1 producer 1 consumer (200K msgs per producer) | 2.54 | 0.86 | 0.92 |\n| 1 producer 10 consumers (200K msgs per producer) | 4.00 | 1.39 | 1.36 | \n| 10 producers 1 consumer (100K msgs per producer) | 13.19 | 6.74 | 0.94 |\n| 3 producers 20 consumers (100K msgs per producer) | 9.30 | 2.22 | 2.17 |\n| 20 producers 3 consumers (50K msgs per producer) | 18.62 | 7.41 | 0.64 |\n| 20 producers 20 consumers (50K msgs per producer) | 36.51 | 1.32 | 3.79 |\n\n\n##### System #2 (Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz, 2 cores, Ubuntu 18.04)\n\n*(measured execution times in seconds)*\n\n| | multiprocessing.Queue | faster-fifo, get() | faster-fifo, get_many() |\n|---------------------------------------------------|:---------------------:|:-----------------------:|:-------------------------:|\n| 1 producer 1 consumer (200K msgs per producer) | 7.86 | 2.09 | 2.2 |\n| 1 producer 10 consumers (200K msgs per producer) | 11.68 | 4.01 | 3.88 | \n| 10 producers 1 consumer (100K msgs per producer) | 44.48 | 16.68 | 5.98 |\n| 3 producers 20 consumers (100K msgs per producer) | 22.59 | 7.83 | 7.49 |\n| 20 producers 3 consumers (50K msgs per producer) | 66.3 | 22.3 | 6.35 |\n| 20 producers 20 consumers (50K msgs per producer) | 78.75 | 14.39 | 15.78 |\n\n## Run tests\n\n`python -m unittest`\n\n(there are also C++ unit tests, should run them if C++ code was altered)\n\n## Recent PyPI releases\n\n##### v1.4.6\n\n* Added missing `<cstdio>` causing issues with newer g++. Thank you [mesaglio](https://github.com/mesaglio)!\n\n##### v1.4.5\n\n* Added method `data_size()` to query the total size of the messages in queue (in bytes). Thank you [@LucaNicosia](https://github.com/LucaNicosia)!\n\n##### v1.4.4\n\n* Fixed an obscure issue with the TLSBuffer ctor being called without arguments (guessing it's Cython's weirdness)\n\n##### v1.4.3\n\n* Simplified usage with \"spawn\" multiprocessing context. No need to use `faster_fifo_reduction` anymore. Thank you [@MosBas](https://github.com/MosBas)!\n\n##### v1.4.2\n\n* Fixed an issue with the custom Queue pickler\n\n##### v1.4.1\n\n* Fixed multithreading issues using threading.local for message recv buffer (huge thanks to [@brianmacy](https://github.com/brianmacy)!)\n* Better error reporting in Cython and C++\n* Added threading tests\n\n##### v1.4.0\n\n* Increase default receive buffer size from 10 bytes to 5000 bytes.\n\n##### v1.3.1\n\n* Minor change: better debugging messages + improved C++ tests\n\n##### v1.3.0\n* Now support custom serializers and deserializers instead of Pickle (thank you [@beasteers](https://github.com/beasteers)!):\n```Python\nq = Queue(max_size_bytes=100000, loads=custom_deserializer, dumps=custom_serializer)\n```\n\n## Footnote\n\nOriginally designed for SampleFactory, a high-throughput asynchronous RL codebase https://github.com/alex-petrenko/sample-factory.\n\nProgrammed by [Aleksei Petrenko](https://alex-petrenko.github.io/) and Tushar Kumar at [USC RESL](https://robotics.usc.edu/resl/people/).\n\nDeveloped under MIT License, feel free to use for any purpose, commercial or not, at your own risk.\n\nIf you wish to cite this repository:\n\n```\n@misc{faster-fifo,\n author={Petrenko, Aleksei and Kumar, Tushar},\n title={A Faster Alternative to Python's multiprocessing.Queue},\n publisher={GitHub},\n journal = {GitHub repository},\n howpublished = {\\url{https://github.com/alex-petrenko/faster-fifo}},\n year={2020},\n}\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A faster alternative to Python's standard multiprocessing.Queue (IPC FIFO queue)",
"version": "1.4.6",
"project_urls": {
"Homepage": "https://github.com/alex-petrenko/faster-fifo"
},
"split_keywords": [
"multiprocessing",
"data",
"structures"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "faf26f36b83383d0f2f20ffc5af696177e70c7aaa0b713d291e6817b4522371d",
"md5": "a4778f736670274408c6b182932ca3e0",
"sha256": "7d01a7a5a43701ea49a6eca085b14a0b4dfe616713cbd960d5628f6078fc1ea1"
},
"downloads": -1,
"filename": "faster_fifo-1.4.6.tar.gz",
"has_sig": false,
"md5_digest": "a4778f736670274408c6b182932ca3e0",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 112545,
"upload_time": "2024-05-25T08:14:44",
"upload_time_iso_8601": "2024-05-25T08:14:44.934319Z",
"url": "https://files.pythonhosted.org/packages/fa/f2/6f36b83383d0f2f20ffc5af696177e70c7aaa0b713d291e6817b4522371d/faster_fifo-1.4.6.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-05-25 08:14:44",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "alex-petrenko",
"github_project": "faster-fifo",
"travis_ci": true,
"coveralls": false,
"github_actions": true,
"lcname": "faster-fifo"
}