Готовность против завершения Async IO Использование памяти?

12

Я смотрел этот разговор о внедрении Async IO в Rust, и Карл упоминал две потенциальные модели. Готовность и Завершение.

Модель готовности:

  • вы говорите ядру, что хотите прочитать из сокета
  • делать другие вещи на некоторое время ...
  • ядро говорит вам, когда сокет готов
  • ты читаешь (заполняешь буфер)
  • делай что хочешь
  • освободить буфер (происходит автоматически с Rust)

Модель завершения:

  • Вы выделяете буфер для ядра, чтобы заполнить
  • делать другие вещи на некоторое время ...
  • ядро говорит вам, когда буфер был заполнен
  • делать с данными все, что нужно
  • освободить буфер

В примере Карла с использованием модели готовности вы можете перебирать готовые сокеты, заполняя и освобождая глобальный буфер, из-за которого кажется, что он будет использовать гораздо меньше памяти.

Теперь мои предположения:

Под капотом (в пространстве ядра), когда сокет называется «готовым», данные уже существуют. Он вошел в сокет по сети (или откуда-либо), и ОС хранит данные.

Это не так, как будто это волшебное распределение не происходит в модели готовности. Просто ОС абстрагируется от вас. В модели Завершения ОС просит вас выделить память до того, как данные действительно поступят, и станет ясно, что происходит.

Вот моя исправленная версия модели готовности:

  • вы говорите ядру, что хотите прочитать из сокета
  • делать другие вещи на некоторое время ...
  • ПОПРАВКА: данные поступают в ОС (некоторое место в памяти ядра)
  • ядро говорит вам, что сокет готов
  • вы читаете (заполняете другой буфер отдельно от буфера ядра abover (или вы получаете указатель на него?))
  • делай что хочешь
  • освободить буфер (происходит автоматически с Rust)

/ Мои предположения

Мне нравится держать программу пользовательского пространства маленькой, но я просто хотел кое-что прояснить, что на самом деле происходит здесь. Я не вижу, чтобы одна модель по своей природе использовала меньше памяти или поддерживала более высокий уровень параллельного ввода-вывода. Я хотел бы услышать мысли и более глубокое объяснение этого.

kjs3
источник
Я также прибыл сюда из той беседы на YouTube. Для любого, кто узнает о том, как выполнять асинхронный ввод-вывод или как реализовывать циклы событий, команда Rust имеет этот плейлист «Aysnc Interviews», здесь собеседование с очень знающими людьми из сообщества
cacoder

Ответы:

5

В модели готовности потребление памяти пропорционально количеству данных, не потребляемых приложением.

В модели завершения потребление памяти пропорционально количеству ожидающих вызовов сокетов.

Если имеется много сокетов, которые в основном простаивают, то модель готовности потребляет меньше памяти.

Для модели завершения легко исправить: инициировать чтение 1 байта. Это потребляет только крошечный буфер. Когда чтение завершится, выполните еще одно (возможно, синхронное) чтение, которое получит оставшиеся данные.

На некоторых языках Модель Завершения чрезвычайно проста в реализации. Я считаю, что это хороший выбор по умолчанию.

USR
источник
1

В модели Завершения ОС просит вас выделить память до того, как данные действительно поступят, и станет ясно, что происходит.

Но что произойдет, если поступит больше данных, чем выделено для вас места? Ядро все еще должно выделить свой собственный буфер, чтобы не отбрасывать данные. (Например, именно поэтому работает 1-байтовый трюк чтения, упомянутый в ответе пользователя.)

Компромисс состоит в том, что, хотя модель завершения потребляет больше памяти, она также может (иногда) выполнять меньше операций копирования, потому что сохранение буфера означает, что аппаратное обеспечение может напрямую выводить DMA из него или в него. Я также подозреваю (но менее уверен), что Модель Завершения имеет тенденцию выполнять фактическую операцию копирования (если она существует) в другом потоке, по крайней мере для IOCP Windows, в то время как Модель Готовности делает это как часть неблокирующей read()или write()вызов.

rpjohnst
источник