2013-03-10 11 views
7

Którędy do utworzenia ciągów znaków jest więcej czasu wydajnych w C#wydajności tworzenia Łączone ciągi

Numer 1:

bool value = true; 
int channel = 1; 
String s = String.Format(":Channel{0}:Display {1}", channel, value ? "ON" : "OFF"); 

numer 2:

bool value = true; 
String channel = "1"; 
string s = ":Channel" + channel + ":Display " + value ? "ON" : "OFF"; 
+0

Zdaję sobie sprawę, że zadaję prawie niemożliwe, ale DLACZEGO nie próbowałeś się zmierzyć? – zerkms

+2

Czy zidentyfikowałeś to jako problem z wydajnością? Wybierz bardziej czytelny (IMO 'Format') –

+0

Dlaczego spadki? To przyzwoite pytanie (nawet gdybyś mógł je zmierzyć sam, oto, może pomóc innym). –

Odpowiedz

7

Zamierzam dodać przykład teraz, aby zilustrować jak kompilator traktuje dwa, ponieważ nie wydaje się być LOT zamieszanie w niektórych innych odpowiedzi (edit: pamiętać, że wiele z tych nieporozumień zostały usunięte/edytowane away):

bool value = true; 
int channel = 1; 
String s = String.Format(":Channel{0}:Display {1}", channel, 
    value ? "ON" : "OFF"); 

ostatnia linia jest kompilowany jako:

String s = String.Format(":Channel{0}:Display {1}", 
    new object[2] {(object)channel, value ? "ON" : "OFF")}; 

nocie co ciekawe stworzenia tablicy i „box” dla int channel i oczywiście dodatkowe przetwarzanie wymagane do znalezienia {0}/{1}.

teraz Numer 2:

bool value = true; 
String channel = "1"; 
string s = ":Channel" + channel + ":Display " + (value ? "ON" : "OFF"); 

Ostatni wiersz jest kompilowany jako:

string s = string.Concat(":Channel", channel, ":Display ", value ? "ON" : "OFF"); 

tutaj nie ma żadnej tablicy, no box (channel jest ciągiem), a nie analizowania. Przeciążenie to public static string string.Concat(string,string,string,string); string.Concat jest zaimplementowany bardzo wydajnie, wstępnie przypisuje odpowiedni ciąg znaków z wyprzedzeniem, a następnie nadpisywanie itp.

W większości przypadków kod jest w porządku. Druga wersja jest bardziej wydajna pod względem technicznym (bez ramki, bez tablic, bez analizowania), ale jest poważnym problemem, jeśli chcesz później umiędzynarodowić. W większości aplikacji nie zauważysz żadnej różnicy między nimi. Przetwarzanie jest szybkie, a pole/tablica są zbierane tanio jako obiekty zerowe. Ale tanie nie jest darmowe.

6

To może pomóc sam to przetestujesz. Zostało to wykonane przy użyciu środowiska wykonawczego .net v4.0.30319.

sw = new System.Diagnostics.Stopwatch(); 

// Number 1 
bool value = true; 
int channel = 1; 
sw.Start(); 
for (int i = 0; i <= 100000; i++) 
{ 
    String s = String.Format(":Channel{0}:Display {1}", channel, value ? "ON" : "OFF"); 
} 
sw.Stop(); 

sw.Reset(); 

// Number 2 
sw.Start(); 
for (int i = 0; i <= 100000; i++) 
{ 
    string s = "Channel" + channel + ":Display " + (value ? "ON" : "OFF"); 
} 
sw.Stop(); 

Mój wynik to:

Numer 1: 136ms

Ilość 2: 91ms

więc druga opcja ma lepszą wydajność. Fakt, że pierwsza opcja wykorzystuje dodatkowe wywołanie metody (string.Format()) i zastępowanie parametrów (jak zauważył Marc), to różnica.

Jeśli zamiast używać 100.000 iteracji używam 1.000.000, to co mam

numer 1: 1308ms

Ilość 2: 923ms

Zasadniczo , ten sam wniosek.

+1

Osobiście nie mam tego 16ms vs 12ms jest wystarczająco wyraźny, z uwagi na dokładność taktowania –

+0

@MarcGravell: Przepraszam, podczas pisania tego komentarza dodałem kolejny przykład z 100 000 iteracjami. –

+1

To bardziej jak to;) teraz wszystko, co musisz zrobić, to uruchomić go na dokładnej wersji .NET (pod-) OP, wersji OS (pod) i dokładnego sprzętu, dokładnie w tych samych warunkach obciążenia tła ... (ok, teraz dokuczam) –

-4

Numer 1 jest najbardziej wydajny.

Funkcja String.Format() używa wewnętrznie typu System.Text.StringBuilder do łączenia ciągów.

StringBuilder jest używany do reprezentowania ciągów zmiennego (aka edytowalne) i jest najskuteczniejszym sposobem do konkretyzacji ciągów. http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx.

+1

to rozumowanie jest niekompletne; druga wersja używa 'string.Concat', który jest * również * mocno zoptymalizowany. Zapewne jeszcze bardziej, ponieważ używa raczej szybkiego przydzielania i nadpisywania, niż zmienionego bufora buforowego. Jeśli byłaby to pętla, to pewnie: 'StringBuilder' wygrałby rozdanie. Dla konkluzji jednolinijkowej: nie; 'string.Concat' jest najbardziej odpowiednim podejściem, które można wykonać za pomocą kodu pokazanego w drugim przykładzie. –

+0

Od [MSDN] (http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx) 'Użyj klasy String, jeśli łączysz stałą liczbę obiektów String. W takim przypadku kompilator może nawet łączyć poszczególne operacje konkatenacji w jedną operację. " – Dzienny

+1

Twoja zmiana sprawia, że ​​ta odpowiedź jest gorsza i aktywnie niepoprawna. Przepraszam, ale nie sądzę, że rozumiesz, jak kompilator traktuje drugi przykład. Podpowiedź: to nie jest to, co opisujesz. –

0

Odnośnie odpowiedzi Marca Gravella: Możesz uniknąć operacji boksu, przekazując kanał.ToString(). Potwierdziłem emperatywnie, że daje to niewielką poprawę wydajności zarówno dla pierwszego, jak i drugiego poziomu.

  • W przypadku nr 1 unika się operacji boksu.
  • W przypadku nr 2 unikasz wywoływania niejawnego operatora konwersji.

Ale po próbie obalenia twojej teorii, (z dłuższymi konkatenacjami i losowymi ciągami) muszę przyznać, że # 2 jest szybsze niż # 1.