首页 > 解决方案 > 并行下载和提取,最大化性能?

问题描述

我想下载并提取 100 个 tar.gz 文件,每个文件大小为 1GB。目前,我已经通过多线程和通过内存字节流避免磁盘 IO 来加速它,但是任何人都可以告诉我如何让它更快(只是为了好奇)?

from bs4 import BeautifulSoup
import requests
import tarfile

import multiprocessing as mp
from concurrent.futures import ThreadPoolExecutor

# speed up by only extracting what we need
def select(members):
    for file in members:  
        if any(ext in file.name for ext in [".tif", ".img"]):
            yield file

# for each url download the tar.gz and extract the necessary files
def download_and_extract(x):
    # read and unzip as a byte stream
    r = requests.get(x, stream=True)
    tar = tarfile.open(fileobj=r.raw, mode='r|gz')
    tar.extractall(members=select(tar))
    tar.close()


# parallel download and extract the 96 1GB tar.gz files
links = get_asset_links()
# 3 * cpu count seemed to be fastest on a 4 core cpu
with ThreadPoolExecutor(3 * mp.cpu_count()) as executor:
    executor.map(download_and_extract, links)

我目前的方法需要 20 - 30 分钟。我不确定理论上可能的加速是多少,但如果它有帮助,单个文件的下载速度是 20 MB/s。

如果有人能满足我的好奇心,那将不胜感激!我研究的一些东西是 asyncio、aiohttp 和 aiomultiprocess、io.BytesIO 等。但我无法让它们与 tarfile 库很好地配合使用。

标签: multithreadingasynchronousparallel-processingdownloadtar

解决方案


您的计算可能是IO bound。压缩通常是一项缓慢的任务,尤其是 gzip 算法(新算法可以更快)。根据提供的信息,平均读取速度约为 70 Mo/s。这意味着存储吞吐量至少约为 140 Mo/s。它看起来完全正常和预期。如果您使用 HDD 或慢速 SSD,则尤其如此。

除此之外,由于选择members. 请记住,tar gz 文件是将一大块文件打包在一起,然后用 gzip 压缩。要遍历文件名,tar 文件需要已经部分解压缩。tarfile关于(可能的缓存)的实现,这可能不是问题。如果所有丢弃文件的大小都很小,最好简单地解压缩整个存档文件,然后删除要丢弃的文件。另外,如果你的内存很大,而且所有丢弃的文件都不小,你可以先将文件解压到内存中的虚拟存储设备中,以便写入丢弃的文件。这可以在 Linux 系统上本地完成。


推荐阅读