Если ваша цель — обработать построчно один большой текстовый файл при помощи aiofiles, то, скорее всего, вам следует использовать привычный способ чтения, а именно синхронную функцию open(). Скорость будет несколько выше, чем при использовании асинхронной функции open(). Это происходит потому, что в asyncio многие методы являются «обертками» над методами стандартных библиотек Python. Это с одной стороны позволяет поддерживать привычный синтаксис, а с другой стороны несколько усложняет выполнение тех же самых операций ради поддержки асинхронности.
Давайте сравним работу методов на практике. Для примера кода будет использоваться файл file_1.txt, в котором 100 000 строк. Это позволит наглядно проверить скорость чтения в обычном и асинхронном стилях.
import time
import aiofiles
import asyncio
async_time = float()
def sync_read():
start = time.perf_counter()
with open(f'file_1.txt', 'r') as file:
for line in file.readlines():
print(line.strip())
sync_time = round(time.perf_counter() - start, 5)
return sync_time
async def async_read():
start = time.perf_counter()
async with aiofiles.open('file_1.txt', mode='r') as file:
for line in await file.readlines():
print(line.strip())
async_time = round(time.perf_counter() - start, 5)
return print(f"Файл считан построчно в асинхронном стиле за {async_time} сек")
sync = sync_read()
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(async_read())
print(f"Файл считан построчно в синхронном стиле за {sync} сек")
Вывод:
Файл считан построчно в асинхронном стиле за 0.93855 сек Файл считан построчно в синхронном стиле за 0.92342 сек
В этом примере мы видим, что чтение в синхронном стиле заняло немного меньше времени, чем в асинхронном. Различие незначительное, но оно есть.
В следующем примере мы создадим и построчно прочитаем сразу 1000 текстовых файлов в каждом напишем
«Hello, world!». Как думаете, какой подход победит по скорости?
import os
import time
import asyncio
import aiofiles
# Создание директории для файлов, если она не существует
os.makedirs('test_files', exist_ok=True)
# Создание 1000 текстовых файлов
def create_files():
for i in range(1000):
with open(f'test_files/file_{i}.txt', 'w') as f:
f.write('Hello, world!\n' * 100) # Запись некоторого количества строк для создания содержимого
create_files() # Создание файлов
# Синхронное чтение файлов
def sync_read_files():
start = time.perf_counter()
for i in range(1000):
with open(f'test_files/file_{i}.txt', 'r') as f:
lines = f.readlines()
sync_time = time.perf_counter() - start
return sync_time
# Асинхронное чтение файлов
async def async_read_file(file_path):
async with aiofiles.open(file_path, mode='r') as f:
await f.readlines()
async def async_read_files():
start = time.perf_counter()
tasks = [async_read_file(f'test_files/file_{i}.txt') for i in range(1000)]
await asyncio.gather(*tasks)
async_time = time.perf_counter() - start
return async_time
def main():
sync_time = sync_read_files() # Синхронное чтение
async_time = asyncio.run(async_read_files()) # Асинхронное чтение
print(f"Синхронное чтение 1000 файлов заняло {sync_time:.5f} секунд")
print(f"Асинхронное чтение 1000 файлов заняло {async_time:.5f} секунд")
if __name__ == '__main__':
main()
Видим, что результат асинхронного подхода превзошёл синхронный в несколько раз. У вас финальные цифры могут отличаться; они зависят от скорости вашего железа. Какой же вывод можно сделать из этого? Вывод таков: нет смысла работать с одним файлом в асинхронном стиле. Асинхронность идеально подходит для работы с множественным вводом/выводом, и нужно чётко понимать и различать, где стоит её применять, а где нет. Такая же ситуация сохранится, если мы захотим скачать большое видео из интернета.
Конечно, такой код не может быть использован без цикла событий или в синхронной программе. Давайте изменим его, добавим в него всё необходимое и скачаем свой первый файл с помощью aiofiles.
Пример работы с файлом видео:
import aiofiles
import asyncio
import aiohttp
async def async_write(url):
async with aiohttp.ClientSession() as session:
async with aiofiles.open('video/async_video_async.mp4', mode='wb') as video:
async with session.get(url) as response:
async for piece in response.content.iter_chunked(5120):
await video.write(piece)
url = 'https://mob25.com/nu_pogodi.mp4'
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(async_write(url))