Если ваша цель — обработать построчно один большой текстовый файл при помощи 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))