You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Asyncio下载大文件比线程慢10倍:求优化方案与原因分析

大文件异步下载性能问题分析与优化方案

问题背景

使用ThreadPoolExecutor实现的多线程图片下载(线程池最大3个工作线程)处理5MB以上的大体积图片时运行正常,但相同场景下基于asyncio+aiohttp+aiofiles的异步实现速度极慢,且代码无语法或逻辑错误,需要分析性能瓶颈原因并给出优化方案。

性能瓶颈原因

  1. 大文件一次性读取阻塞事件循环
    原异步代码中使用await response.read()一次性将整个大文件加载到内存,虽然该操作为异步,但读取过程中事件循环会被长时间占用,无法切换到其他下载任务。而线程池方案中,单个线程的IO阻塞不会影响其他线程执行,多线程可并行处理多个下载任务,因此大文件场景下线程池调度效率更高。
  2. 内存占用过高间接拖慢性能
    一次性读取大文件会导致内存占用激增,不仅增加GC压力,还可能引发系统层面的内存交换,进一步降低整体运行速度。
  3. 默认配置未适配大文件场景
    aiohttp默认的连接池、超时等参数为通用配置,未针对大文件下载做优化,并发处理效率受限。

优化方案

核心优化思路是分块处理网络读取与文件写入,让事件循环能及时切换任务,同时调整异步客户端配置提升并发效率。

修改后的异步下载代码

import asyncio
import os
import time
import aiohttp
import aiofiles

async def download_image_async(url, folder, session):
    os.makedirs(folder, exist_ok=True)
    filename = url.split('/')[-1]
    filepath = os.path.join(folder, filename)
    
    # 分块读取网络内容,每块1MB
    chunk_size = 1024 * 1024
    async with session.get(url) as response:
        async with aiofiles.open(filepath, "wb") as f:
            async for chunk in response.content.iter_chunked(chunk_size):
                await f.write(chunk)

async def main():
    # 调整aiohttp连接池参数,增大并发数(需根据目标服务器限制调整,避免触发反爬)
    connector = aiohttp.TCPConnector(limit=10)
    async with aiohttp.ClientSession(connector=connector) as session:
        urls = [
            "https://images-assets.nasa.gov/image/PIA03149/PIA03149~orig.jpg",
            "https://upload.wikimedia.org/wikipedia/commons/3/37/African_Bush_Elephant.jpg",
            "https://upload.wikimedia.org/wikipedia/commons/9/97/The_Earth_seen_from_Apollo_17.jpg",
            "https://upload.wikimedia.org/wikipedia/commons/2/29/%22Arte_Colonial%22.jpg",
            "https://upload.wikimedia.org/wikipedia/commons/d/d2/%22CUT%22_June_1929_04.jpg",
            "https://upload.wikimedia.org/wikipedia/commons/8/82/%22CUT%22_June_1929_05.jpg",
            "https://upload.wikimedia.org/wikipedia/commons/b/b1/%22Council_of_the_Gods%22_in_Galleria_Borghese_%28Rome%29_ceiling.jpg",
            "https://upload.wikimedia.org/wikipedia/commons/7/71/%22East_front_of_Court_House_and_Planter%27s_House.%22.jpg",
            "https://upload.wikimedia.org/wikipedia/commons/b/b6/%22Greater_Germany%22._Major_administrative_divisions._July_1._1944._100_kms_-_btv1b531213280.jpg",
        ] * 2
        tasks = [download_image_async(url, "3_asyncio_opt", session) for url in urls]
        await asyncio.gather(*tasks)

if __name__ == "__main__":
    start_time = time.time()
    asyncio.run(main())
    end_time = time.time()
    print(f"优化后Asyncio download time: {end_time - start_time:.2f} seconds")

优化点说明

  • 分块读取与写入:使用response.content.iter_chunked(chunk_size)分块读取网络数据,每块1MB,读取一块就写入一块,避免一次性加载大文件到内存,让事件循环有机会切换到其他任务,提升并发效率。
  • 调整连接池参数:通过TCPConnector(limit=10)增大并发连接数(需根据目标服务器的并发限制调整,避免被封禁),提升多任务并行处理能力。
  • 提前创建目录:将目录创建逻辑移到文件操作前,避免每个分块写入时重复判断目录存在性。

内容的提问来源于stack exchange,提问作者Daniil Yefimov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 14:14:54