python 多线程 加速处理列表数据

背景

最近使用大模型进行推理时,首先将大模型 api 部署,然后借助于 request库,发送请求。

在之前写同步的请求,必须等到上一次request响应的结果回来后,才能发送下一次请求,这样导致速度非常慢。

由于大模型部署支持并发,故打算采用多线程方式,让大模型快速处理。

简介

  • 多线程

多线程示例代码

python 复制代码
import concurrent
from concurrent.futures import ThreadPoolExecutor  
from tqdm import tqdm  
import time  

start = time.time()
# 假设我们有一个URL列表,这里用占位符代替  
urls = [i for i in range(25)]  

# 模拟发送网络请求  
def send_request(url):
    print(f"发送请求 {url}")
    time.sleep(1)  # 模拟网络延迟
    print(f"收到请求 {url} 的响应")
    return url

# 使用ThreadPoolExecutor创建线程池,并限制线程数为 max_workers
with ThreadPoolExecutor(max_workers=5) as executor:  
    # 使用executor的map方法将urls列表中的每个URL映射到send_request函数  
    # 这会自动将任务分配给线程池中的线程,而且 results_iterator 收集到的返回值的结果
    results_iterator = executor.map(send_request, urls)
    
end = time.time()

print(f"总共耗时 {end - start} 秒")
python 复制代码
print(list(results_iterator))

results_iterator 收集到的返回值是有序的

多线程的运行结果如下所示:

bash 复制代码
发送请求 0
发送请求 1
发送请求 2
发送请求 3
发送请求 4
收到请求 0 的响应收到请求 3 的响应
发送请求 5
收到请求 4 的响应
发送请求 6

发送请求 7
收到请求 1 的响应
发送请求 8
收到请求 2 的响应
发送请求 9
收到请求 5 的响应
发送请求 10
收到请求 6 的响应
发送请求 11
收到请求 7 的响应
发送请求 12
收到请求 9 的响应
发送请求 13
收到请求 8 的响应
发送请求 14
...
收到请求 24 的响应
收到请求 22 的响应

总共耗时 5.021236896514893 秒

关于 核心数选取多少:

在Python中,多线程的性能并不总是随着线程数的增加而线性提升,尤其是在执行CPU密集型任务时。这是因为Python的全局解释器锁(GIL)限制了一次只能有一个线程执行Python字节码。所以,即使你的机器有多个CPU核心,Python的多线程在并行执行CPU密集型任务时可能并不会充分利用所有的核心。

然而,如果你的线程主要执行I/O操作(如网络请求、文件读写等),那么多线程可以有效地利用等待时间,从而提高程序的总体效率。在这种情况下,线程数可以超过CPU核心数,因为线程会在等待I/O操作时自动切换。

对于CPU密集型任务,如果你想要充分利用多核CPU的性能,更好的选择是使用多进程(multiprocessing)而不是多线程。Python的multiprocessing库可以创建多个进程,每个进程都有自己的Python解释器,从而可以并行执行CPU密集型任务。

至于具体应该设置多少线程或进程,这取决于你的具体需求和机器的配置。一般来说,对于I/O密集型任务,线程数可以设置为略多于CPU核心数,以便在等待I/O时充分利用CPU。对于CPU密集型任务,进程数可以设置为等于或略少于CPU核心数,以避免过多的上下文切换开销。但请注意,这只是一个一般性的建议,具体的最佳值可能需要通过实验来确定。

对比 不使用多线程

python 复制代码
start = time.time()
# 假设我们有一个URL列表,这里用占位符代替  
urls = [i for i in range(25)]  

# 定义一个函数来发送网络请求  
def send_request(url):
    print(f"发送请求 {url}")
    time.sleep(1)  # 模拟网络延迟
    print(f"收到请求 {url} 的响应")
    return url

ans = []
for url in urls:
    ans.append(send_request(url))
    
end = time.time()

print(f"总共耗时 {end - start} 秒")
bash 复制代码
发送请求 0
收到请求 0 的响应
发送请求 1
收到请求 1 的响应
发送请求 2
收到请求 2 的响应
发送请求 3
收到请求 3 的响应
发送请求 4
收到请求 4 的响应
发送请求 5
收到请求 5 的响应
发送请求 6
收到请求 6 的响应
发送请求 7
收到请求 7 的响应
发送请求 8
收到请求 8 的响应
发送请求 9
收到请求 9 的响应
发送请求 10
收到请求 10 的响应
发送请求 11
收到请求 11 的响应
发送请求 12
...
收到请求 23 的响应
发送请求 24
收到请求 24 的响应
总共耗时 25.100407123565674 秒

如果不使用多线程的,必须收到上一次请求的响应,才能发送下一个请求;总共耗时25秒,而多线程只需要5秒;

相关推荐
红色石榴33 分钟前
Qt中文乱码解决
开发语言·qt
Htht11134 分钟前
【Qt】实现模拟触摸屏 上下滑动表格 的两种方式
开发语言·qt
A 八方34 分钟前
Python MongoDB
开发语言·python·mongodb
sz66cm2 小时前
Python基础 -- 使用Python实现ssh终端并实现数据处理与统计功能
开发语言·python·ssh
liangbm34 小时前
MATLAB系列02:MATLAB基础
开发语言·数据结构·笔记·matlab·教程·工程基础·高级绘图
ac-er88885 小时前
如何在Flask中实现国际化和本地化
后端·python·flask
Adolf_19935 小时前
Flask-WTF的使用
后端·python·flask
空城皆是旧梦5 小时前
python爬虫初体验(一)
爬虫·python
藓类少女5 小时前
正则表达式
数据库·python·mysql·正则表达式
change95135 小时前
PHP纯离线搭建(php 8.1.7)
开发语言·php