Производительность ввода-вывода - асинхронно против TPL против потока данных против RX

У меня есть фрагмент кода C# 5.0, который генерирует тонну сетевого и дискового ввода-вывода. Мне нужно запустить несколько копий этого кода параллельно. Какая из следующих технологий может дать мне лучшую производительность:

  • асинхронные методы с ожиданием

  • напрямую используйте Task из TPL

  • нули TPL Dataflow

  • Реактивные расширения

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

3 ответа

Решение

Любая разница в производительности между этими опциями будет несущественной, учитывая "тонну сетевого и дискового ввода-вывода".

Лучше задать вопрос: "Какой вариант легче всего изучить и разработать?" Или "какой вариант будет лучше поддерживать этот код через пять лет?" И для этого я бы предложил async во-первых, или Dataflow или Rx, если ваша логика лучше представлена ​​в виде потока.

Это все равно что пытаться оптимизировать продолжительность трансатлантического полета, попросив самый быстрый способ снять ремень безопасности.

Хорошо, некоторые реальные советы, так как я был придурком

Давайте дадим полезный ответ. Думайте о производительности как о "классах" действий - каждое из них на порядок медленнее (по крайней мере!):

  1. Только доступ к процессору, очень небольшое использование памяти (то есть рендеринг очень простой графики в очень быстрый графический процессор или вычисление цифр числа Пи)
  2. Доступ только к процессору и памяти, ничего на диске (т.е. хорошо написанная игра)
  3. Доступ к диску
  4. Доступ к сети.

Если вы выполняете хотя бы одно из действий № 3, нет смысла проводить оптимизации, типичные для действий № 1 и № 2, такие как оптимизация потоковых библиотек - они полностью омрачены попаданием диска. То же самое для трюков с процессором - если вы постоянно испытываете ошибки в кэше L2/L3, сэкономить несколько циклов ЦП на сборке вручную не стоит (именно поэтому такие вещи, как развертывание циклов, обычно плохая идея в наши дни).

Итак, что мы можем извлечь из этого? Есть два способа сделать вашу программу быстрее: либо перейти с № 3 на № 2 (что не всегда возможно, в зависимости от того, что вы делаете), либо сделать меньше операций ввода-вывода. Скорость ввода-вывода и скорость сети являются фактором, ограничивающим скорость в большинстве современных приложений, и это то, что вы должны пытаться оптимизировать.

Это более старый вопрос, но для тех, кто читает это...

Это зависит. Если вы попытаетесь насытить канал 1 Гбит / с сообщениями 50B, вы будете привязаны к процессору даже при простой неблокирующей отправке через необработанные сокеты. Если, с другой стороны, вы довольны пропускной способностью 1 Мбит / с или ваши сообщения превышают 10 КБ, любая из этих сред выполнит эту работу.

Для ситуаций с низкой пропускной способностью я бы рекомендовал расставить приоритеты по простоте использования, то есть async/await, Dataflow, Rx, TPL в этом порядке. Обратите внимание, что приложение с высокой пропускной способностью должно быть прототипировано, как если бы оно было с низкой пропускной способностью, и оптимизировано позже.

Для настоящего приложения с высокой пропускной способностью я могу порекомендовать Dataflow over Rx, поскольку Rx не рассчитан на высокий параллелизм. Raw TPL - это нижний уровень, который гарантирует минимальные издержки, если вы можете справиться со сложностью. Если вы сможете эффективно использовать выделенные потоки, это будет еще быстрее. Async/await против Dataflow IMO не влияет на производительность. Накладные расходы кажутся сопоставимыми, поэтому выберите тот, который лучше подходит.

Другие вопросы по тегам