0Pricing
C# Academy · Lekcja

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

Szybkie sprawdzenie: Która zmiana zwykle poprawia przepustowość, ale może zwiększyć opóźnienie pojedynczego elementu?

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

  1. Parallel.ForEach i PLINQ
  2. Producent i konsument z użyciem Channels (omówienie)
  3. Kompromis między przepustowością a opóźnieniem
← Powrót do C# Academy