Performa kode asinkron tidak diukur dari kecepatan satu operasi saja, melainkan dari berapa banyak pekerjaan yang dapat berjalan serentak dan berapa banyak alokasi yang dikeluarkan untuk mencapainya. Stopwatch memang bisa dipakai, tetapi angkanya berubah-ubah antar mesin. Ukuran yang lebih objektif adalah tingkat konkurensi maksimum: berapa pekerjaan yang benar-benar aktif bersamaan pada satu batch. Bab ini mengukurnya secara deterministik, lalu merangkum keputusan teknis yang memengaruhi performa.

Ukuran dan Keputusan Utama #

  • Konkurensi puncak: Task.WhenAll memulai semua pekerjaan sebelum menunggu, sehingga pekerjaan aktif bersamaan; await berurutan menjalankan satu per satu.
  • ValueTask<T> untuk jalur panas: hasil dari cache tidak perlu alokasi objek task, sebagaimana dibahas pada bab Task Return Types.
  • Task.Run hanya untuk komputasi: pekerjaan I/O sudah asinkron secara bawaan; membungkusnya dengan Task.Run hanya menambah pemindahan thread.
  • Gunakan ulang HttpClient: membuat instance baru untuk setiap permintaan menghabiskan sumber daya koneksi.

Contoh: Mengukur Konkurensi Puncak Dua Mode #

Kode berikut menjalankan tiga pekerjaan 60 ms dalam dua mode dan mencatat tingkat konkurensi tertingginya:

csharp
class StatistikKonkurensi
{
    public int Aktif;
    public int Puncak;
}

static async Task KerjaAsync(int jedaMs, StatistikKonkurensi statistik)
{
    lock (statistik)
    {
        statistik.Aktif++;
        if (statistik.Aktif > statistik.Puncak)
        {
            statistik.Puncak = statistik.Aktif;
        }
    }
    await Task.Delay(jedaMs);
    lock (statistik)
    {
        statistik.Aktif--;
    }
}

// Pemanggil:
int puncakBerurutan = await JalankanBatchAsync(paralel: false);
int puncakParalel = await JalankanBatchAsync(paralel: true);

Console.WriteLine($"Konkurensi puncak mode berurutan: {puncakBerurutan}");
Console.WriteLine($"Konkurensi puncak mode paralel: {puncakParalel}");

Output yang Dihasilkan:

text
Konkurensi puncak mode berurutan: 1
Konkurensi puncak mode paralel: 3

Mari kita bedah pengukurannya:

  1. JalankanBatchAsync(paralel: false): mode berurutan menunggu pekerjaan satu per satu dengan await; saat pekerjaan kedua dimulai, pekerjaan pertama sudah berakhir, sehingga puncaknya 1.
  2. JalankanBatchAsync(paralel: true): mode paralel memulai ketiga pekerjaan sebelum menunggu; ketiga Task.Delay aktif bersamaan sehingga puncaknya 3.
  3. lock (statistik): penghitung aktif dibagi antar thread; kunci menjaga penambahan dan pembacaan puncak agar tidak saling menimpa.
  4. statistik.Puncak: nilai puncak menggambarkan manfaat nyata batching; untuk tiga pekerjaan 60 ms, mode paralel selesai sekitar 60 ms sedangkan mode berurutan sekitar 180 ms.

Dengan ukuran yang objektif, keputusan batching, pemilihan tipe hasil, dan penggunaan ulang resource dapat dievaluasi berdasarkan data, bukan dugaan.