-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Status of project and performance improvements #679
Comments
Hey! Requests for better performance and unbound event loop are reasonable, a pull request is welcome. Anyway, thank you for your questions! |
Please correct me if I'm wrong. |
Thanks for your answers. No, You might also be interested in looking at a benchmark that uses event loop methods with asyncio benchmarkimport asyncio
from threading import Thread
from concurrent.futures import CancelledError
def work(loop, in_q, out_q):
try:
while True:
item = asyncio.run_coroutine_threadsafe(in_q.get(), loop).result()
if item is None:
break
loop.call_soon_threadsafe(out_q.put_nowait, item)
except (CancelledError, RuntimeError): # event loop is closed
pass
async def func(in_q, out_q):
ops = 0
try:
item = 42
while True:
await out_q.put(item)
ops += 1
item = await in_q.get()
finally:
print(ops)
async def main():
in_q = asyncio.Queue()
out_q = asyncio.Queue()
Thread(target=work, args=[asyncio.get_running_loop(), out_q, in_q]).start()
try:
await asyncio.wait_for(func(in_q, out_q), 6)
except asyncio.TimeoutError:
pass
finally:
await out_q.put(None)
asyncio.run(main()) In terms of operations per second, |
Thanks, your measures are very interesting. |
I haven't profiled your queues, but by a simple brute force search of possible variants I found that the bottleneck is the sync -> async case, probably due to over-sync in the I don't think there's a silver bullet here. Optimization is likely to require significant changes to the internal architecture. Speaking of which, those benchmarks have one drawback: switching costs skew the results. If we measure the sync -> async case in a single thread, the situation is even worse: the janus benchmarkimport asyncio
import janus
async def func(in_q, out_q):
ops = 0
try:
item = 42
loop = asyncio.get_running_loop()
while True:
loop.call_soon(out_q.put, item)
ops += 1
item = await in_q.get()
finally:
print(ops)
async def main():
queue = janus.Queue()
try:
await asyncio.wait_for(func(queue.async_q, queue.sync_q), 6)
except asyncio.TimeoutError:
pass
asyncio.run(main()) asyncio benchmarkimport asyncio
async def func(in_q, out_q):
ops = 0
try:
item = 42
loop = asyncio.get_running_loop()
while True:
loop.call_soon_threadsafe(out_q.put_nowait, item)
ops += 1
item = await in_q.get()
finally:
print(ops)
async def main():
queue = asyncio.Queue()
try:
await asyncio.wait_for(func(queue, queue), 6)
except asyncio.TimeoutError:
pass
asyncio.run(main()) aiologic benchmarkimport asyncio
import aiologic
async def func(in_q, out_q):
ops = 0
try:
item = 42
loop = asyncio.get_running_loop()
while True:
loop.call_soon(out_q.green_put, item)
ops += 1
item = await in_q.async_get()
finally:
print(ops)
async def main():
queue = aiologic.SimpleQueue()
try:
await asyncio.wait_for(func(queue, queue), 6)
except asyncio.TimeoutError:
pass
asyncio.run(main()) Speaking of the But what confuses me is that answering questions in this repository can take months. This is strange for a maintained project with few issues: I wouldn't ask about the status of project if I didn't see some issues being answered after seven months, and not even by a contributor. It would be nice if there was clarity in this regard, as otherwise it's not clear where the problem lies. And yes, thank you for responding so quickly. |
Here's an idea: we can try to implement your queues on top of my primitives. That way all the complex logic would stay in my package and your implementation would be simplified. As a side effect, your queues will start supporting My primitives are not API compatible for the reason that otherwise the implementation would get complicated and some things would just get ugly. For example, my queues ( |
Well, I just took the |
First, please let me describe the status quo. Now I've found a time to return back. I have no guarantee to support it forever but I have an intention to do again more or less well. Everyone should decide want one use the library or not. |
Regarding If you want to make a pull request with some improvements -- you are welcome! |
OK, I understand. Your project is stable, so I don't want to break backwards compatibility either. But I don't agree that your implementation is easier and safer by definition. The reason is simple: your code tries to use something similar to async-aware thread-aware conditions, but they are not defined as a separate entity. Which makes it harder to understand the implementation and detect bugs. My variant, which I've measured above, uses the same implementation as the In my opinion, it's much easier when asynchronous code differs from synchronous code only in how waiting is performed. And it's also safer: the implementation of primitives is simplified and has only the logic necessary for their operation (and I've also done a lot of theoretical work to support cancellation and timeouts, so don't worry). And it's even faster, as I've shown. The waits themselves are presented as one-time events that solve only one task: to wake up a thread or task when it's needed. In fact, this is an application of the single-responsibility principle, which is one of the SOLID principles. Since I think that the queue I described could still be useful, let's decide this: I'll publish my alternative implementation as a separate package, with an interface inspired by Janus. That way we won't have to break the backward compatibility of this package, and we can close some of the issues. But I'll need your help to add a link to the package in your README, because it's very hard to distribute such unpopular solutions nowadays, and without it users are unlikely to know that their wishes are already implemented elsewhere. |
If you are asking for a link --yes, sure. |
Now available as Culsans. |
Great! Is there any chance to publish the test suite along with the source code? |
Yes, I'll publish tests and benchmarks as soon as I have enough time. I removed |
Done. Code coverage is 99%, tests and benchmarks are published along with the source code. Concurrency libraries other than |
As I can see from the commits, there hasn't been a single major change since December 2021, with the maximum changes being config fixes, cleanup of types, and dropping support for Python 3.7. The project's issues are not resolved, and pull requests are still open. So the question is: what is the level of support for this project now?
There's a reason why I'm asking this. I too have implemented queues (as part of a more general package) that support communicating between synchronous and asynchronous code, but with a different approach. In simple put-get tests between two threads in both directions, they appear to be ~7x faster on CPython, ~24x faster on PyPy, which mitigates the performance issue. They are not bound to the event loop. They also support various concurrency libraries, and a lot more.
janus benchmark
aiologic benchmark
I need to know what the status of your package is to determine how best to contrast mine with yours.
The text was updated successfully, but these errors were encountered: