Wprowadzenie

Jest to pierwszy z serii kilku artykułów wpis dotyczący pisania wydajnego kodu w języku C#. Od pewnego czasu jestem ściśle związany z językiem JavaScript i technologiami czysto webowymi. Jednakże w trakcie tego projeku jeden z klientów zaczął zgłaszać problemy z wydajnością i czasem przetwarzania danych w starszej wersji aplikacji. Okazało się, że dziennie system (w jego przypadku) przetwarza blisko 20 milionów rekordów...

Z pozoru nie zastanawiamy się na oszczędnością kilku milisekund przy przetwarzaniu danych. Każdy z nas przywykł do pewnej implementacji i nie zastanawia się czy są lepsze rozwiązania. W tym jednak wypadku należało prześledzić dokładnie kod, porównać różne podejścia i wyciągnać stosowne wnioski. Seria tych artykułów ma na celu podsumowanie tych obserwacji. Pamiętajcie jednak, że pomiary, który będziemy dokonywać (a raczej różnice między nimi) są naprawdę znaczące dopiero przy przetwarzania ogromnynych ilości danych.

String Split

W przypadku języka C# mamy do dyspozycji 6 przeciążeń metody Split(...):

public String[] Split(params char[] separator);
public String[] Split(char[] separator, int count);
public String[] Split(char[] separator, StringSplitOptions options);
public String[] Split(char[] separator, int count, StringSplitOptions options);
public String[] Split(String[] separator, StringSplitOptions options);
public String[] Split(String[] separator, int count, StringSplitOptions options);
Większość z nas używa poniższej implementacji:
string text = "Audi|RS6";
string[] test = text.Split('|');
Zastanawialiście się kiedyś czy jest to najwydajniesze rozwiązanie?

Zmienimy nasze podejście: zamiast bezpośredniego przekazania znaku stworzymy tablicę znaków a nasz separator przekażemy jako element zdefiniowanej tablicy:

string[] test2 = text.Split(new char[] { '|' });
W powyższym podejściu pomijamy etap interpetacji związany z wywołaniem dodatkowego kodu (związanego ze słowem kluczowym params, które przyjmuje zmienną liczbę parametrów):
public String[] Split(char[] separator, int count, StringSplitOptions options);
W przypadku naszej implementacji pojedyczny element jest interpretowany jako jednoczęściowa tablica znaków.

Sprawdźmy teraz w praktyce porównianie czasów wykonania powyższych implementacji:

static void Main(string[] args)
{
    Stopwatch s1 = new Stopwatch();
    s1.Start();
    string text = "Audi|RS6";
    string[] test = text.Split('|');
    Console.WriteLine(s1.ElapsedTicks.ToString());

    Stopwatch s2 = new Stopwatch();
    s2.Restart();
    string[] test2 = text.Split(new char[] { '|' });
    Console.WriteLine(s2.ElapsedTicks.ToString());

    Console.ReadKey();
}
Różnica jest naprawdę znacząca: Metoda String Split

Podsumowanie

Wnioski nasuwają się same: implementacja do której przywykliśmy nie zapewnia największej wydajności a wraz z przetwarzaniem dużej ilości danych możemy zauważyć istotne zmiany w czasie wykonania naszego programu.