Python aiofiles vs aiofile

Для асинхронной работы с файлами в python на основе asyncio есть две библиотеки aiofiles и aiofile. Какую использовать?

Обе библиотеки не являются истинно асинхронными

Как это ни будет печально для истиного асинхрониста, эти библиотеки просто превращают асинхронные вызовы в потоки. Да да, чем больше откроете файлов, тем больше будет потоков в программе, и хотя это делается незаметно для программиста, по сути получаем просто асинхронный синтаксис, скрывающий многопоточность.

Теоретическое подтверждение

aiofiles - version 0.4.0

эта библиотека напрямую превращает обычные синхронные вызовы в асинхронные с помощью выполнения в потоках, достаточно глянуть исходник:

@delegate_to_executor('close', 'flush', 'isatty', 'read', 'read1', 'readinto'....

этот делегат в конце концов вызывает ._loop.run_in_executor

aiofile - version 1.5.2

эта библиотека с помощью cython использует POSIX AIO, который так написан, что превращает вызовы в потоки.

Вывод

Если POSIX AIO переделают, или подменить POSIX AIO на какой нибудь другой AIO, то у aiofile есть шансы стать истинно асинхронным. Aiofiles без переписывания никогда не станет истинно асинхронным.

Практическое подтверждение

Для подтверждения нам понадобится жесткий диск с медленным доступом, чтобы неспеша насладиться зрелищем потоков в htop. Воспользуемся инструкциями из статьи эмуляция медленного жесткого диска, создадим 3 файла: data1.txt data2.txt data3.txt

aiofiles - run_aiofiles.py

import aiofiles, asyncio

async def read(i):
    async with aiofiles.open(f'/mnt/local_share/data{i}.txt') as f:
        contents = await f.read()

asyncio.get_event_loop().run_until_complete(asyncio.wait( [read(1),read(2),read(3)] ))

aiofile - run_aiofile.py

import asyncio
from aiofile import AIOFile

async def read(i):
    async with AIOFile(f'/mnt/local_share/data{i}.txt', 'r') as f:
        contents = await f.read()

asyncio.get_event_loop().run_until_complete(asyncio.wait( [read(1),read(2),read(3)] ))

Использование

В удобстве обе более менее одинаковы, пример записи в файл aiofiles

async def write_async():
    txt = ''.join(random.choices(string.ascii_uppercase + string.digits, k=1000))
    async with aiofiles.open('/var/bigdata/guru.txt', mode='w') as f:
        await f.write(txt)
        await f.close()

пример записи в файл aiofile

async def write_async():
    txt = ''.join(random.choices(string.ascii_uppercase + string.digits, k=1000))
    async with AIOFile('/var/bigdata/guru.txt', 'w') as afp:
        await afp.write(txt)
        await afp.fsync()

Будем записывать раз в секунду 1000 байт в файл, результат получился интересный:

aiofiles, IO = 4.1 KB/sec aiofile, IO = 32 KB/sec

IO = 4.1 KB/sec вполне ожидаемо, это блок минимального файла, странно что для aiofile получается IO = 32 KB/sec

Яндекс.Метрика