Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Asyncpal: Preemptive concurrency and parallelism for sporadic workloads (github.com/pyrustic)
28 points by alexrustic 35 days ago | hide | past | favorite | 8 comments
Hi HN ! Alex here. I'm excited to show you Asyncpal (https://github.com/pyrustic/asyncpal), a Python library for parallelism [1] and preemptive concurrency [2] tailored for sporadic workloads [3].

I've been working on a private project where it would be convenient to have asynchronous versions of some operations without using `async/await` [4]. Besides `async/await`, which represents cooperative concurrency via the `asyncio` package, Python also provides preemptive concurrency through its `threading` package. Additionally, the `concurrent.futures` package builds upon the `threading` package to provide a thread pool [5], which is the design pattern my project needed for managing concurrent tasks.

Although a thread pool is the right tool for the problems it solves, its creation and usage involve the allocation of resources that must be properly released. For this reason, it is recommended to use a thread pool with a context manager [6] to ensure that resources are properly released once the pool executor has finished its tasks.

However, this strategy can introduce overhead in programs that sporadically submit tasks to a thread pool, as multiple pools may be created and destroyed throughout the execution of these programs.

Maintaining one or a few thread pools for the duration of a program can be an effective solution, assuming these thread pools can automatically shrink after workers have been idle for a short period defined by the programmer.

I developed Asyncpal to meet the requirements and extended it with processes to achieve parallelism. For more details about this project, please refer to the README. You can download the lightweight package on PyPI, try the examples provided in the README, or run the tests on your machine.

Let me know what you think about this project.

[1] https://en.wikipedia.org/wiki/Parallel_computing

[2] https://en.wikipedia.org/wiki/Concurrent_computing

[3] https://www.cloudcomputingpatterns.org/unpredictable_workloa... (related)

[4] https://en.wikipedia.org/wiki/Async/await

[5] https://en.wikipedia.org/wiki/Thread_pool

[6] https://superfastpython.com/threadpoolexecutor-context-manag...




I was asked hours ago by a concurrency enthusiast, whose website has been a valuable source of information for me on the topic, to tell in a sentence what capability Asyncpal provides to users above the Python standard library (stdlib).

I found the question interesting because it goes straight to the point and calls for a concise answer. I believe the answer is missing from this 'Show HN'. Here is my response to the question:

Asyncpal unifies the stdlib (concurrent.futures + multiprocessing.pool) and provides true elastic pools (grow + shrink) with an intuitive interface.


If I'm given a chance to develop, I would start with the last part of the sentence that might sound subjective. For example, the 'Future' class in 'concurrent.futures' exposes a method named 'result' to collect the result of a task. In contrast, the 'Future' class in Asyncpal exposes a 'collect' method and a 'result' property.

The stdlib's pools only grow in size and do not shrink, making them non-elastic. Therefore, I would not want to keep them alive in the background for sporadic workloads [1].

Discussions on 'concurrent.futures' vs 'multiprocessing.pool.Pool' highlight that each has unique features. While 'concurrent.futures' is the modern package, it omits some niceties found in 'multiprocessing.pool.Pool'. For example, 'concurrent.futures' has only one 'map' [2] method, which works eagerly and therefore not suitable for very long iterables [3][4]. However, I acknowledge the superiority of 'concurrent.futures.Future' [5] over 'multiprocessing.pool.AsyncResult' [6] because tasks cannot be cancelled with the latter (among other things).

[1] https://www.cloudcomputingpatterns.org/unpredictable_workloa... (related)

[2] https://docs.python.org/3/library/concurrent.futures.html#co...

[3] https://docs.python.org/3/library/multiprocessing.html#multi...

[4] https://docs.python.org/3/library/multiprocessing.html#multi...

[5] https://docs.python.org/3/library/concurrent.futures.html#fu...

[6] https://docs.python.org/3/library/multiprocessing.html#multi...


Looks very well designed. As someone new to asynchronous programming in python, I have been using asyncio.to_thread in a task. Although the best way to manage things is to use a thread pool executor


Thanks for your comment !

When you are doing parallelism, don't forget to protect the 'entry point' of the program by using "if __name__ == '__main__'", and also avoid the __main__.py file [1]

  # this file isn't `__main__.py` !
  from asyncpal import ProcessPool

  def square(x):
      return x**2

  if __name__ == "__main__":  # very important !
      with ProcessPool(4) as pool:
          numbers = range(1000)
          # note that 'map_all' isn't lazy
          iterator = pool.map(square, numbers)  # map is lazy
          result = tuple(iterator)
          assert result == tuple(map(square, numbers))

[1] https://discuss.python.org/t/why-does-multiprocessing-not-wo...


that's cool. but i'm looking for a preemptive concurrency like golang in pyhton that schedules routines and get threads back when routine runs a blocking syscall. the hard part is detection of syscalls. is there any idea?


If I understand correctly, goroutines = asyncio (non-invasive) + asyncio.to_thread (when needed, but automatic). So, it is still cooperative concurrency that feels preemptive due to its design. This Go capability is interesting, and it 'seems' that it cannot be replicated without some integration with the runtime of the target language, i.e., a 'baked-in solution'. For now, I'm fine with true preemptive concurrency, but this might change in the future.


This is sweet. I will be using this going forward. Love the simplicity of managing everything


Thank you !




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: