我向一个 api 接口,用 requests 访问四万次,线程池设置为 20 的时候,跑完花了 440 秒,设置为 30 的时候,花了 570 秒,设为 10 的时候最快,只用了 277 秒。 为什么这里并发越高,执行的还越慢呢?
我向一个 api 接口,用 requests 访问四万次,线程池设置为 20 的时候,跑完花了 440 秒,设置为 30 的时候,花了 570 秒,设为 10 的时候最快,只用了 277 秒。 为什么这里并发越高,执行的还越慢呢?
1
ClericPy Oct 23, 2019
class ThreadPoolExecutor(_base.Executor):
# Used to assign unique thread names when thread_name_prefix is not supplied. _counter = itertools.count().__next__ def __init__(self, max_workers=None, thread_name_prefix='', initializer=None, initargs=()): """Initializes a new ThreadPoolExecutor instance. Args: max_workers: The maximum number of threads that can be used to execute the given calls. thread_name_prefix: An optional name prefix to give our threads. initializer: An callable used to initialize worker threads. initargs: A tuple of arguments to pass to the initializer. """ if max_workers is None: # Use this number because ThreadPoolExecutor is often # used to overlap I/O instead of CPU work. max_workers = (os.cpu_count() or 1) * 5 这里源码里都写了建议线程池大小了 对 Python 来说, 多线程并不会利用多核, 所以一堆线程是靠系统的不断切换来确定线程完成状态的, 切换的多了自然开销就大了, 性能损失也就大了 多进程也是一个道理, 你就俩核心的话, 不断切换来切换去, 那 CPU 大部分时间都在切换状态上, 根本没时间干活, 也会变慢 权威一点的搜 Google , 一个意思 https://www.google.com.hk/search?q=Python+best+thread+pool+size https://stackoverflow.com/questions/42541893/maximum-pool-size-when-using-threadpool-python |
2
ClericPy Oct 23, 2019
requests 只是让代码写起来快, 跑起来想快还是得考虑其他的, 比如用 gevent 代替 threading, 比如用 aiohttp 代替 requests, aiohttp 的 qps 不开 uvloop 的情况下是 requests + threading 的三倍多, 虽然 golang 原生 net/http 是 requests 十几倍...
这个年代了, 协程的 CPU 利用率高, 状态切换开销小, 不用考虑并发限制(也可以手动干预限制), 很少考虑多线程竞态的锁关系, 学点也不吃亏 |
3
Achilless OP @ClericPy 感谢指导,话说您的意思是说用协程的话就不会出现这种切换开销大导致的效率低下么,或者说就算出现也是在更大的并发下,比如 100
|
4
xiaolinjia Oct 23, 2019
协程其实是单线程里的轮询机制。
|
5
ClericPy Oct 24, 2019
@Achilless 协程的切换是用户主动操作的精确切换, 没有太多多余开销, 而且内存也比较省, CPU 效率更高, 至于并发, 没什么压力, 可以去 Google 看看 Python coroutine 的相关文章, 内部实现比较复杂, 但是对用户的接口依然是 Future 那套, 不是太难学
|