Z C# 7 został wprowadzony nowy typ a konkretniej struktura ValueTask która jest odpowiednikiem klasy Task. Głównym celem tej struktury jest poprawa wydajności metod asynchronicznych w których ścieżka synchroniczna jest dużo częściej wykonywana niż asynchroniczna 🤔 Spójrzmy na poniższy kod:
1
2
3
4
5
6
7
8
9
10
public async Task<IList<HolidayDetailsDto>> GetHolidaysTask()
{
if (_cachedTaskData == null)
{
_cachedTaskData = await _httpClient.GetFromJsonAsync<IList<HolidayDetailsDto>>("https://date.nager.at/api/v2/publicholidays/2022/PL");
}
return _cachedTaskData;
}
Kod jest prosty, jeżeli nie mamy danych w cachu to pobieramy je, jeżeli są to od razu je zwracamy. Czyli kod synchroniczny – zwracanie danych z cacha, będzie dużo częściej wykonywany niż kod asynchroniczny – pobieranie danych z jakiegoś serwera. Poniżej kod który używa już ValueTask:
1
2
3
4
5
6
7
8
9
10
public async ValueTask<IList<HolidayDetailsDto>> GetHolidaysValueTask()
{
if (_cachedValueTaskData == null)
{
_cachedValueTaskData = await _httpClient.GetFromJsonAsync<IList<HolidayDetailsDto>>("https://date.nager.at/api/v2/publicholidays/2022/PL");
}
return _cachedValueTaskData;
}
Jedyna różnica to tylko zwracany typ. Jak to ma się do wydajności? ⤵️
ValueTask vs Task – wydajność
Za pomocą poniższego kodu porównałem wydajność Task i ValueTask. Wydajność mierzyłem za pomocą BenchmarkDotNet.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Benchmark]
public async ValueTask ValueTask()
{
for (int i = 0; i < 5000; i++)
{
var result = await GetHolidaysValueTask();
}
}
[Benchmark]
public async Task Task()
{
for (int i = 0; i < 5000; i++)
{
var result = await GetHolidaysTask();
}
}
Wynik: Jak widzimy, szybkość działania metod jest bardzo podobna jednak użycie pamięci (kolumna Allocated) jest dużo mniejsze gdy używamy ValueTask.
Kod powyższego benchmarku znajdziecie tutaj: https://github.com/Carq/PerformanceLab/tree/master/ValueTaskVsTask
Podsumowanie – kiedy używać
ValueTask należy używać gdy w aplikacji korzystamy z metod asynchronicznych w których ścieżka synchroniczna jest dużo częściej wykonywana niż asynchroniczna. Używając ValueTask możemy zaoszczędzić prawie połowę pamięci.
Comments powered by Disqus.