Kompromis między przepustowością a opóźnieniem
Proszę równoważyć łączną pracę na sekundę (przepustowość) z czasem obsługi jednego elementu (opóźnieniem): porównać przetwarzanie elementów pojedynczo, przetwarzanie partiami i dostrajanie stopnia równoległości.
Kompromis między przepustowością a opóźnieniem to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 3 z 3. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 3 lekcji w sumie.
Przepustowość a opóźnienie
Definicje:
- Przepustowość: liczba elementów na sekundę
- Opóźnienie: czas potrzebny na zakończenie przetwarzania jednego elementu
- Kompromis: przetwarzanie partiami i większa równoległość mogą zwiększyć przepustowość, ale opóźnić poszczególne elementy
Styl przetwarzania element po elemencie
Każdy element jest przetwarzany po jego nadejściu: oczekiwanie na pojedynczy element jest minimalne, ale narzut powtarza się dla każdego elementu.
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);
}
}
Styl przetwarzania partiami
Przetwarzanie partiami ogranicza powtarzający się narzut i zwiększa przepustowość, ale pierwsze elementy czekają na zapełnienie partii, co zwiększa opóźnienie.
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);
}
}
Dostosowywanie równoległości
Dostosowanie wartości MaxDegreeOfParallelism może zwiększyć przepustowość pracy ograniczonej przez CPU; zbyt wysoka wartość może ją zmniejszyć z powodu przełączania kontekstu.
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);
}
}
}
Idea mikropartii
Mikropartie mogą pomóc osiągnąć oba cele: zapewniają większą przepustowość niż przetwarzanie element po elemencie i mniejsze opóźnienie niż bardzo duże partie.
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);
}
}
Wskazówki dotyczące dostrajania
Wytyczne dotyczące dostrajania:
- Należy mierzyć zarówno ms/item, jak i items/sec
- W pierwszej kolejności należy wypróbować mikropartie
- Należy powoli zwiększać równoległość i obserwować użycie CPU oraz przełączanie kontekstu
- Należy ograniczać pojemność kolejek, aby uniknąć długiego oczekiwania i wzrostu zużycia pamięci
Kompromis między przepustowością a opóźnieniem
Podsumowanie
Podsumowanie: Przetwarzanie element po elemencie oznacza małe opóźnienie i mniejszą przepustowość. Duże partie lub wysoki DOP oznaczają większą przepustowość i większe opóźnienie. Mikropartie oraz staranne dostrajanie DOP pomagają zachować równowagę między tymi celami.
Często zadawane pytania
Czy lekcja „Kompromis między przepustowością a opóźnieniem” jest bezpłatna?
Tak — pełny tekst „Kompromis między przepustowością a opóźnieniem” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 3 lekcji w sumie.
Co nauczysz się w „Kompromis między przepustowością a opóźnieniem”?
Proszę równoważyć łączną pracę na sekundę (przepustowość) z czasem obsługi jednego elementu (opóźnieniem): porównać przetwarzanie elementów pojedynczo, przetwarzanie partiami i dostrajanie stopnia ró… Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć C# Academy?
Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 3.
Ile czasu zajmuje lekcja „Kompromis między przepustowością a opóźnieniem”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?
Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Parallel.ForEach i PLINQ
- Producent i konsument z użyciem Channels (omówienie)
- Kompromis między przepustowością a opóźnieniem