如何避免嵌套for循环以加速图片批量下载代码?
代码加速方案解析
你的代码运行缓慢的核心原因不是两层循环的复杂度——每个用户固定对应43张图,总操作数是「用户数×43」,属于线性复杂度。真正拖慢速度的是:每次调用os.system都会启动一个新的gsutil进程,进程启动有额外开销,且所有下载任务串行执行,完全没利用网络带宽的并行性。
下面是具体的优化方案:
优化1:简化文件名生成,去掉冗余判断
用Python字符串格式化语法统一生成文件名,无需分i<10的分支判断:
# 自动生成形如 person_0001、person_0010 的格式 suffix = f"{i:04d}"
优化2:多线程并行下载(推荐)
文件下载属于IO密集型任务,多线程能大幅提升下载效率。结合subprocess模块(比os.system更高效可控),示例代码如下:
import os import pandas as pd from concurrent.futures import ThreadPoolExecutor import subprocess def download_image(person, i): suffix = f"{i:04d}" src_path = f"gs://[My_Bucket_name]/{person}_{suffix}/image.png" dst_path = f"./data/image_folder/{person}_{suffix}.png" # 直接调用gsutil进程,避免启动shell的额外开销 subprocess.run( ["gsutil", "cp", src_path, dst_path], check=True, capture_output=True ) df = pd.read_csv("/Desktop/images_csv_file.csv") os.makedirs("./data/image_folder/", exist_ok=True) # 生成所有待下载的任务组合 tasks = [] for person in df.person_id: for i in range(1, 44): tasks.append((person, i)) # 线程数可根据网络带宽调整,比如20-50 with ThreadPoolExecutor(max_workers=20) as executor: executor.map(lambda args: download_image(*args), tasks)
优化3:批量执行gsutil命令
如果不想用多线程,可将多个复制命令合并为一次gsutil调用,减少进程启动次数:
import os import pandas as pd import subprocess df = pd.read_csv("/Desktop/images_csv_file.csv") os.makedirs("./data/image_folder/", exist_ok=True) # 整理所有源路径和目标路径 src_dst_list = [] for person in df.person_id: for i in range(1, 44): suffix = f"{i:04d}" src_dst_list.append(f"gs://[My_Bucket_name]/{person}_{suffix}/image.png") src_dst_list.append(f"./data/image_folder/{person}_{suffix}.png") # 一次性执行批量复制 subprocess.run(["gsutil", "cp"] + src_dst_list, check=True)
注意:如果文件总数极多(上万级),该方法可能因命令行参数过长报错,此时可拆分批次执行,或结合多线程方案。
额外建议
- 移除
sudo:除非gsutil必须通过sudo才能执行,否则不要添加——sudo会增加进程启动开销,还可能引发权限问题。 - 增加错误处理:可在
download_image函数中添加try-except块,记录下载失败的任务并支持重试。
内容的提问来源于stack exchange,提问作者Caesar
相关产品推荐
相关产品推荐

