Tak, różnica w wydajności jest znacząca. Zobacz artykuł z bazy wiedzy „ Jak poprawić wydajność konkatenacji ciągów w Visual C # ”.
Zawsze starałem się najpierw napisać kod dla zachowania przejrzystości, a następnie zoptymalizować go pod kątem wydajności. To o wiele łatwiejsze niż robienie tego na odwrót! Jednak widząc ogromną różnicę wydajności w moich aplikacjach między tymi dwoma, teraz myślę o tym nieco ostrożniej.
Na szczęście stosunkowo łatwo jest przeprowadzić analizę wydajności kodu, aby zobaczyć, gdzie spędzasz czas, a następnie zmodyfikować go, aby używał w StringBuilder
razie potrzeby.
Aby wyjaśnić, co Gillian powiedziała o 4 łańcuchach, jeśli masz coś takiego:
wtedy byłoby szybciej przy użyciu ciągów i operatora plusa. Wynika to z faktu, że (podobnie jak Java, jak wskazuje Eric), wewnętrznie używa StringBuilder automatycznie (w rzeczywistości używa prymitywów, których używa StringBuilder)
Jeśli jednak to, co robisz, jest bliższe:
Następnie musisz jawnie użyć StringBuilder. .Net nie tworzy tutaj automatycznie StringBuilder, ponieważ byłoby to bezcelowe. Na końcu każdej linii „a” musi być (niezmiennym) łańcuchem, więc musiałby utworzyć i umieścić StringBuilder w każdej linii. Aby uzyskać szybkość, musisz używać tego samego StringBuilder, dopóki nie skończysz budować:
źródło
a
jest zmienną lokalną, a obiekt, do którego się odwołuje, nie został przypisany do żadnej innej zmiennej (która może być dostępna dla innego wątku), dobry optymalizator może stwierdzić, że żaden inny koda
nie uzyskuje dostępu podczas tej sekwencji linie; tylko końcowa wartość spraw. Mogłoby to potraktować te trzy wiersze kodu tak, jakby były napisane .a
a = b + c + d;
StringBuilder jest preferowany, JEŚLI robisz wiele pętli lub widelców w swoim kodzie przepustowym ... jednak dla wydajności PURE, jeśli możesz uniknąć SINGLE deklaracji ciągu, to jest to o wiele bardziej wydajne.
Na przykład:
jest bardziej wydajny niż
W takim przypadku StringBuild można uznać za łatwiejsze w utrzymaniu, ale nie jest ono bardziej wydajne niż deklaracja pojedynczego ciągu.
9 razy na 10 ... użyj konstruktora ciągów.
Na marginesie: string + var jest również bardziej wydajny niż string.Formatowanie (ogólnie), które korzysta z StringBuilder wewnętrznie (w razie wątpliwości ... sprawdź reflektor!)
źródło
Prosty przykład pokazujący różnicę prędkości przy stosowaniu
String
konkatenacji vsStringBuilder
:Wynik:
Wynik:
W rezultacie pierwsza iteracja zajęła 15423 ms, a druga iteracja
StringBuilder
zajęła 10 ms.Wydaje mi się, że używanie
StringBuilder
jest szybsze, dużo szybsze.źródło
Ten test porównawczy pokazuje, że regularne łączenie jest szybsze przy łączeniu 3 lub mniejszej liczby łańcuchów.
http://www.chinhdo.com/20070224/stringbuilder-is-not-always-faster/
StringBuilder może znacznie poprawić wykorzystanie pamięci, szczególnie w przypadku dodania 500 łańcuchów razem.
Rozważ następujący przykład:
Co dzieje się w pamięci? Tworzone są następujące ciągi znaków:
Dodając te pięć liczb na końcu łańcucha, stworzyliśmy 13 obiektów łańcuchowych! I 12 z nich było bezużytecznych! Łał!
StringBuilder rozwiązuje ten problem. Nie jest to „zmienny ciąg”, jak często słyszymy ( wszystkie ciągi w .NET są niezmienne ). Działa poprzez utrzymanie wewnętrznego bufora, tablicy znaków. Wywołanie Append () lub AppendLine () dodaje ciąg do pustego miejsca na końcu tablicy char; jeśli tablica jest zbyt mała, tworzy nową, większą tablicę i kopiuje tam bufor. Tak więc w powyższym przykładzie StringBuilder może potrzebować tylko jednej tablicy do przechowywania wszystkich 5 dodatków do łańcucha - w zależności od wielkości bufora. Możesz powiedzieć StringBuilder, jak duży powinien być jego bufor w konstruktorze.
źródło
i.ToString()
. Zatem w StringBuilder nadal musisz tworzyć ciągi 6 + 1; zredukował tworzenie 13 łańcuchów do utworzenia 7 łańcuchów. Ale to wciąż niewłaściwy sposób patrzenia na to; utworzenie ciągów sześciu liczb nie jest istotne. Konkluzja: po prostu nie powinieneś wspominać o sześciu ciągach utworzonych przezi.ToString()
; nie są one częścią porównania wydajności.Tak,
StringBuilder
daje lepszą wydajność podczas powtarzania operacji na łańcuchu. Dzieje się tak, ponieważ wszystkie zmiany są wprowadzane w jednym wystąpieniu, dzięki czemu można zaoszczędzić dużo czasu zamiast tworzyć nowe, takie jak wystąpienieString
.String Vs Stringbuilder
String
System
przestrzeni nazwStringBuilder
(zmienny ciąg)System.Text
przestrzeni nazwźródło
String Vs String Builder:
Po pierwsze musisz wiedzieć, w którym zgromadzeniu żyją te dwie klasy?
Więc,
ciąg jest obecny w
System
przestrzeni nazw.i
StringBuilder jest obecny w
System.Text
przestrzeni nazw.Do deklaracji ciągu :
Musisz dołączyć
System
przestrzeń nazw. coś takiego.Using System;
i
Dla StringBuilder deklaracji :
Musisz dołączyć
System.text
przestrzeń nazw. coś takiego.Using System.text;
Teraz przyjdź aktualne pytanie.
Jaki jest differene pomiędzy ciągiem & StringBuilder ?
Główną różnicą między tymi dwoma jest to, że:
strunowy jest niezmienny.
i
StringBuilder można modyfikować.
Teraz omówmy różnicę między niezmienną a zmienną
Zmienny: oznacza zmienny .
Niezmienny: oznacza, że nie można go zmienić.
Na przykład:
W tym przypadku zamierzamy zmienić ten sam obiekt 5 razy.
Więc oczywiste pytanie jest takie! Co tak naprawdę dzieje się pod maską, kiedy zmieniamy ten sam ciąg 5 razy.
Tak się dzieje, gdy zmieniamy ten sam ciąg 5 razy.
spójrzmy na postać.
Wyjaśnienie:
Kiedy po raz pierwszy inicjujemy tę zmienną „name” na „Rehan” tj
string name = "Rehan"
Ta zmienna jest tworzona na stosie „name” i wskazuje na tę wartość „Rehan”. po wykonaniu tej linii: „name = name +„ Shah ”. zmienna referencyjna nie wskazuje już na ten obiekt„ Rehan ”, teraz wskazuje na„ Shah ”i tak dalej.string
Jest zatem niezmienne, że po utworzeniu obiektu w pamięci nie możemy go zmienić.Tak więc, kiedy konatynizujemy
name
zmienną, poprzedni obiekt pozostaje tam w pamięci i tworzony jest kolejny nowy obiekt łańcuchowy ...Tak więc z powyższej figury mamy pięć obiektów, cztery obiekty są wyrzucane, w ogóle nie są używane. Wciąż pozostają w pamięci i wykorzystują ilość pamięci. „Garbage Collector” jest odpowiedzialny za to, aby oczyścić zasoby z pamięci.
Tak więc w przypadku łańcucha znaków w dowolnym momencie, gdy manipulujemy nim w kółko, mamy kilka obiektów utworzonych i pozostają tam w pamięci.
Oto historia zmiennego łańcucha.
Teraz spójrzmy na obiekt StringBuilder. Na przykład:
W tym przypadku zamierzamy zmienić ten sam obiekt 5 razy.
Więc oczywiste pytanie jest takie! Co tak naprawdę dzieje się pod maską, kiedy zmieniamy ten sam StringBuilder 5 razy.
Tak się dzieje, gdy zmieniamy ten sam StringBuilder 5 razy.
spójrzmy na postać.
Wyjaśnienie: W przypadku obiektu StringBuilder. nie dostaniesz nowego obiektu. Ten sam obiekt zostanie zmieniony w pamięci, więc nawet jeśli zmienisz obiekt i powiedzmy 10 000 razy, nadal będziemy mieć tylko jeden obiekt stringBuilder.
Nie masz dużo obiektów śmieciowych ani niereferencjonowanych obiektów stringBuilder, ponieważ można to zmienić. Jest zmienny, co oznacza, że zmienia się z czasem?
Różnice:
źródło
StringBuilder zmniejsza liczbę przydziałów i przypisań kosztem dodatkowej pamięci używanej. Właściwie użyte, może całkowicie wyeliminować potrzebę kompilacji do przydzielania coraz większych ciągów znaków, aż do znalezienia wyniku.
vs.
źródło
Źródło: MSDN
źródło
StringBuilder
jest lepszy do budowania łańcucha z wielu niestałych wartości.Jeśli budujesz ciąg z wielu stałych wartości, takich jak wiele wierszy wartości w dokumencie HTML lub XML lub innych fragmentach tekstu, możesz uniknąć dodawania do tego samego ciągu, ponieważ prawie wszystkie kompilatory „stałe składanie”, proces zmniejszania parsowania drzewa, gdy masz kilka ciągłych manipulacji (jest również używany, gdy piszesz coś takiego
int minutesPerYear = 24 * 365 * 60
). A w prostych przypadkach z dołączonymi do siebie niestałymi wartościami kompilator .NET zredukuje kod do czegoś podobnego do tego, coStringBuilder
robi.Ale gdy kompilator nie może zredukować Twojego dodatku do czegoś prostszego, będziesz chciał
StringBuilder
. Jak wskazuje fizch, jest to bardziej prawdopodobne w pętli.źródło
Wierzę, że StringBuilder jest szybszy, jeśli masz więcej niż 4 ciągi, które musisz dołączyć razem. Plus może robić fajne rzeczy, takie jak AppendLine.
źródło
W .NET StringBuilder jest nadal szybszy niż dodawanie ciągów. Jestem prawie pewien, że w Javie po prostu dodają StringBuffer pod maską, gdy dodasz ciągi, więc tak naprawdę nie ma różnicy. Nie jestem pewien, dlaczego jeszcze tego nie zrobili w .NET.
źródło
Rozważ „ Smutną tragedię teatru mikrooptymalizacji ”.
źródło
Używanie ciągów do konkatenacji może prowadzić do złożoności środowiska wykonawczego w kolejności od
O(n^2)
.Jeśli używasz a
StringBuilder
, musisz wykonać o wiele mniej kopiowania pamięci. DziękiStringBuilder(int capacity)
możesz zwiększyć wydajność, jeśli potrafisz oszacować, jak duży będzie finałString
. Nawet jeśli nie jesteś precyzyjny, prawdopodobnie będziesz musiał zwiększyć pojemnośćStringBuilder
tylko kilka razy, co może również pomóc w wydajności.źródło
Widziałem znaczny wzrost wydajności dzięki użyciu
EnsureCapacity(int capacity)
wywołania metody w instancjiStringBuilder
przed użyciem go do przechowywania ciągów znaków. Zwykle nazywam to w wierszu kodu po utworzeniu wystąpienia. Ma taki sam efekt, jakbyś utworzył taką instancjęStringBuilder
:To połączenie alokuje potrzebną pamięć z wyprzedzeniem, co powoduje mniej alokacji pamięci podczas wielu
Append()
operacji. Musisz zgadnąć, ile pamięci potrzebujesz, ale w przypadku większości aplikacji nie powinno to być zbyt trudne. Zwykle mylę się po stronie trochę za dużej pamięci (mówimy około 1k).źródło
EnsureCapacity
bezpośrednio poStringBuilder
utworzeniu instancji. Po prostu instancjęStringBuilder
tak:var sb = new StringBuilder(int capacity)
.W nawiązaniu do poprzednich odpowiedzi, pierwszą rzeczą, którą zawsze robię, gdy myślę o takich sprawach, jest stworzenie małej aplikacji testowej. Wewnątrz tej aplikacji wykonaj test czasowy dla obu scenariuszy i przekonaj się, co jest szybsze.
IMHO, dołączając ponad 500 wpisów w łańcuchach, zdecydowanie należy użyć StringBuilder.
źródło
String i StringBuilder są w rzeczywistości niezmienne, StringBuilder ma wbudowane bufory, które pozwalają na bardziej efektywne zarządzanie jego rozmiarem. Kiedy StringBuilder musi zmienić rozmiar, następuje ponowne przydzielenie go na stercie. Domyślnie ma wielkość 16 znaków, możesz ustawić to w konstruktorze.
na przykład.
StringBuilder sb = new StringBuilder (50);
źródło
Łączenie łańcuchów będzie cię kosztować więcej. W Javie możesz użyć StringBuffer lub StringBuilder w zależności od potrzeb. Jeśli chcesz zsynchronizowaną i bezpieczną dla wątków implementację, wybierz StringBuffer. Będzie to szybsze niż konkatenacja ciągów.
Jeśli nie potrzebujesz implementacji synchronicznej lub bezpiecznej dla wątków, wybierz StringBuilder. Będzie to szybsze niż konkatenacja String, a także szybsze niż StringBuffer, ponieważ nie jest to narzut związany z synchronizacją.
źródło
StringBuilder jest prawdopodobnie preferowany. Powodem jest to, że przydziela więcej miejsca niż obecnie potrzebne (ustawiasz liczbę znaków), aby zostawić miejsce na przyszłe dodatki. Następnie te przyszłe załączniki, które mieszczą się w bieżącym buforze, nie wymagają alokacji pamięci ani odśmiecania, co może być kosztowne. Zasadniczo używam StringBuilder do łączenia złożonych łańcuchów lub formatowania wielokrotnego, a następnie przekształcam się w normalny ciąg znaków, gdy dane są kompletne, i chcę ponownie obiektu niezmiennego.
źródło
Jeśli wykonujesz dużo łączenia łańcuchów, użyj StringBuilder. Podczas łączenia z ciągiem za każdym razem tworzony jest nowy ciąg, zużywając więcej pamięci.
Alex
źródło
Zasadą ogólną jest to, że jeśli muszę ustawić wartość ciągu więcej niż jeden raz lub jeśli ciąg jest dołączany, to musi to być kreator ciągu. Widziałem aplikacje, które napisałem w przeszłości, zanim dowiedziałem się o konstruktorach łańcuchów, które miały ogromny ślad pamięci, który po prostu wydaje się stale rosnąć. Zmiana tych programów na użycie konstruktora ciągów znacznie zmniejsza zużycie pamięci. Teraz przysięgam na konstruktora strun.
źródło
Moje podejście zawsze polegało na użyciu StringBuilder podczas łączenia 4 lub więcej ciągów LUB Gdy nie wiem, jak mogą się odbywać połączenia.
Dobry artykuł na ten temat dotyczący wydajności tutaj
źródło
StringBuilder
jest znacznie bardziej wydajny, ale nie zobaczysz tej wydajności, chyba że wykonasz dużą ilość modyfikacji łańcucha.Poniżej znajduje się krótki fragment kodu, który stanowi przykład wydajności. Jak widać, naprawdę zaczyna się zauważać znaczny wzrost wydajności, gdy przejdziesz do dużych iteracji.
Jak widać, 200 000 iteracji zajęło 22 sekundy, podczas gdy 1 milion iteracji z użyciem tego
StringBuilder
było prawie natychmiastowe.Wynik powyższego kodu:
źródło
StringBuilder będzie działał lepiej, z punktu widzenia pamięci. Jeśli chodzi o przetwarzanie, różnica w czasie wykonania może być nieznaczna.
źródło