【问题标题】:Speed of loading files with asyncio使用 asyncio 加载文件的速度
【发布时间】:2022-11-23 07:17:59
【问题描述】:

我正在编写一段代码,需要将 python 集与许多其他集进行比较,并保留具有最小交集长度的文件的名称。我目前有一个同步版本,但想知道它是否可以从 async/await 中受益。我想从比较集合的加载开始。我写了一个简单的脚本,将一小部分写入磁盘并读入n次数。我很惊讶地看到它的同步版本要快得多。这是可以预料的吗?如果没有,我在下面编码的方式是否存在缺陷?

我的代码如下:

同步版本:

import pickle
import asyncio
import time 
import aiofiles

pickle.dump(set(range(1000)), open('set.pkl', 'wb'))

def count():
    print("Started Loading")
    with open('set.pkl', mode='rb') as f:
        contents = pickle.loads(f.read())
    print("Finishd Loading")

def main():
    for _ in range(100):
        count()

if __name__ == "__main__":
    s = time.perf_counter()
    main()
    elapsed = time.perf_counter() - s
    print(f"{__file__} executed in {elapsed:0.3f} seconds.")

异步版本:

import pickle
import asyncio
import time 
import aiofiles

pickle.dump(set(range(1000)), open('set.pkl', 'wb'))

async def count():
    print("Started Loading")
    async with aiofiles.open('set.pkl', mode='rb') as f:
        contents = pickle.loads(await f.read())
    print("Finishd Loading")

async def main():
    await asyncio.gather(*(count() for _ in range(100)))

if __name__ == "__main__":
    import time
    s = time.perf_counter()
    asyncio.run(main())
    elapsed = time.perf_counter() - s
    print(f"{__file__} executed in {elapsed:0.3f} seconds.")

执行它们导致:

async.py executed in 0.052 seconds.
sync.py executed in 0.011 seconds.

【问题讨论】:

  • 你说异步版本更快,这是应该的,但你发布的时间表明异步版本慢了 5 倍。
  • @OneMadGypsy 抱歉,我的意思是同步速度更快。我现在已经编辑了。
  • 我最近一直在使用异步,通过观察,它似乎只是众多异步 http 请求的游戏规则改变者。我的应用程序目前获取一个提要,然后抓取提要项目中的每一页,因此从长远来看,它最终会为每个提要发出大约 100 个请求。它现在的上限是最多 4 个异步提要。现在我们可能有 400 个请求。异步在这种情况下大放异彩。我个人还没有找到一个“重”到需要异步的本地请求。
  • 你只有一个文件。它被写入某种存储设备。当您打开并读取该文件时,存储该文件的设备必须访问特定位置的特定数据。那将是一个物理瓶颈。我不知道如何并行读取同一个文件 100 次,而且我不希望有任何加速。我认为您观察到的减速是启动 100 个 asyncio 任务的开销,并迫使它们在遇到 await 表达式时来回摆动。这不是一个好的测试概念。
  • @OneMadGypsy 啊,谢谢你提供的信息,这样我就不用自己做所有的测试了!

标签: python asynchronous async-await python-asyncio


【解决方案1】:

aiofiles 是通过使用线程实现的,因此每次您告诉它读取文件时,都会启动一个线程来读取文件。

正在读取的文件实际上非常小,它适合 3 KB,在内存中不到 1 页,也小于核心 L1 缓存,计算机实际上大部分时间都没有从磁盘读取任何内容,它都被移动了在你的部分记忆之间。

在异步情况下,它正在从一个内核的内存移动到第二个,这比将所有内容都保存在一个内核的缓存中要慢,但对于实际从磁盘读取的较大文件和其他需要处理的任务,例如从套接字和从磁盘读取不同的文件并同时进行一些处理你会发现异步版本更快,因为它在引擎盖下使用线程,并且一些任务放弃了 gil,比如从文件和套接字读取,以及一些处理库。

在这两种情况下,您仍在以相同的速度读取文件,因为您将受到驱动器读取速度的限制,您只会减少不读取文件时的“死区时间”,并且您的示例没有“死区-”时间”,它甚至没有从磁盘读取文件。

当您同时从多个 HDD 和 SSD 读取数据时会发生上述异常,其中 1 个线程永远无法足够快地读取数据,因此异步版本会更快,因为它可以同时从多个驱动器读取(假设您有CPU 中的内核和 IO 通道)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-25
    • 1970-01-01
    相关资源
    最近更新 更多