Компромиссы между пропускной способностью и задержкой
Балансируйте общий объем работы в секунду (пропускную способность) и время обработки одного элемента (задержку): сравнивайте поэлементную обработку, пакетирование и настройку степени параллелизма.
«Компромиссы между пропускной способностью и задержкой» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 3 уроков всего.
Пропускная способность и задержка
Определения:
- Пропускная способность: число элементов в секунду
- Задержка: время обработки одного элемента
- Компромисс: пакетная обработка и более высокий параллелизм могут повысить пропускную способность, но увеличить задержку отдельных элементов
Обработка отдельных элементов
Обрабатывайте каждый элемент сразу после поступления: ожидание для отдельного элемента минимально, но накладные расходы повторяются для каждого элемента.
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
// Simulate small per-item cost
static void HandleItem(int x)
{
// Fixed overhead per item
Thread.SpinWait(20000); // tiny CPU work
}
public static void Main(string[] args)
{
int n = 200;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < n; i++)
{
HandleItem(i); // process immediately (no batching)
// emit result right away (low latency style)
}
sw.Stop();
Console.WriteLine("Per-item style: {0} ms for {1} items", sw.ElapsedMilliseconds, n);
}
}
Пакетная обработка
Пакетная обработка сокращает повторяющиеся накладные расходы и повышает пропускную способность, но первые элементы ждут заполнения пакета, что увеличивает задержку.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
public class Program
{
static void ProcessBatch(List<int> batch)
{
// Amortize overhead across the whole batch
Thread.SpinWait(20000); // one-time overhead
for (int i = 0; i < batch.Count; i++)
{
// small per-record work
int val = batch[i] * 2;
if (val == int.MinValue) { } // keep compiler from dropping work
}
}
public static void Main(string[] args)
{
int n = 200;
int batchSize = 20;
List<int> current = new List<int>(batchSize);
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < n; i++)
{
current.Add(i);
if (current.Count == batchSize)
{
ProcessBatch(current);
current.Clear(); // emit results after the batch finishes
}
}
if (current.Count > 0) ProcessBatch(current);
sw.Stop();
Console.WriteLine("Batch style: {0} ms for {1} items (batch={2})", sw.ElapsedMilliseconds, n, batchSize);
}
}
Настройка параллелизма
Настройка MaxDegreeOfParallelism может повысить пропускную способность при работе с высокой нагрузкой на CPU; слишком высокое значение может навредить из-за переключений контекста.
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
static void Work(int x)
{
// CPU-bound unit
Thread.SpinWait(40000);
}
public static void Main(string[] args)
{
int[] data = new int[200];
for (int i = 0; i < data.Length; i++) data[i] = i;
foreach (int dop in new int[] { 1, 2, 4 })
{
var opt = new ParallelOptions();
opt.MaxDegreeOfParallelism = dop;
Stopwatch sw = Stopwatch.StartNew();
Parallel.ForEach(data, opt, Work);
sw.Stop();
Console.WriteLine("DOP={0} -> {1} ms", dop, sw.ElapsedMilliseconds);
}
}
}
Идея микропакетов
Микропакеты помогают сбалансировать обе цели: обеспечивают более высокую пропускную способность, чем обработка отдельных элементов, и меньшую задержку, чем очень крупные пакеты.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
static void ProcessBatch(List<int> batch)
{
Thread.SpinWait(15000); // small shared overhead
for (int i = 0; i < batch.Count; i++) Thread.SpinWait(2000);
}
public static void Main(string[] args)
{
int n = 200;
int micro = 5; // micro-batch size
List<int> buf = new List<int>(micro);
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < n; i++)
{
buf.Add(i);
if (buf.Count == micro)
{
ProcessBatch(buf);
buf.Clear(); // emit more frequently than big batches
}
}
if (buf.Count > 0) ProcessBatch(buf);
sw.Stop();
Console.WriteLine("Micro-batch (size={0}): {1} ms", micro, sw.ElapsedMilliseconds);
}
}
Советы по настройке
Руководство по настройке:
- Измеряйте и мс на элемент, и элементы в секунду
- Сначала попробуйте микропакеты
- Постепенно увеличивайте параллелизм; следите за CPU и переключениями контекста
- Ограничивайте очереди, чтобы избежать длительного ожидания и роста потребления памяти
Компромисс между пропускной способностью и задержкой
Итоги
Итоги: обработка отдельных элементов = низкая задержка и более низкая пропускная способность. Крупные пакеты и высокий DOP = более высокая пропускная способность и большая задержка. Микропакеты и тщательная настройка DOP помогают сбалансировать оба показателя.
Часто задаваемые вопросы
Урок «Компромиссы между пропускной способностью и задержкой» бесплатный?
Да — полный текст урока «Компромиссы между пропускной способностью и задержкой» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 3 уроков всего.
Чему я научусь в уроке «Компромиссы между пропускной способностью и задержкой»?
Балансируйте общий объем работы в секунду (пропускную способность) и время обработки одного элемента (задержку): сравнивайте поэлементную обработку, пакетирование и настройку степени параллелизма. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 3.
Сколько времени занимает урок «Компромиссы между пропускной способностью и задержкой»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Parallel.ForEach и PLINQ
- Производитель и потребитель с каналами (обзор)
- Компромиссы между пропускной способностью и задержкой