Czy String.Contains () jest szybszy niż String.IndexOf ()?

111

Mam bufor ciągów o długości około 2000 znaków i muszę sprawdzić bufor, jeśli zawiera określony ciąg.
Dokonuje sprawdzenia w aplikacji internetowej ASP.NET 2.0 dla każdego żądania internetowego.

Czy ktoś wie, czy metoda String.Contains działa lepiej niż metoda String.IndexOf ?

    // 2000 characters in s1, search token in s2
    string s1 = "Many characters. The quick brown fox jumps over the lazy dog"; 
    string s2 = "fox";
    bool b;
    b = s1.Contains(s2);
    int i;
    i = s1.IndexOf(s2);

Śmieszny fakt

Kb.
źródło
14
Jeśli musisz to zrobić miliard razy na żądanie sieciowe, zacznę przyglądać się takim rzeczom. W każdym innym przypadku nie zawracałbym sobie głowy, ponieważ czas spędzony w którejkolwiek z metod będzie najprawdopodobniej niewiarygodnie nieistotny w porównaniu z otrzymaniem żądania HTTP w pierwszej kolejności.
mookid8000
2
Jednym z kluczy do optymalizacji jest testowanie zamiast zakładania, ponieważ może to zależeć od wielu czynników, takich jak wersja .NET, system operacyjny, sprzęt, zmienność danych wejściowych itp. W wielu przypadkach wyniki testów wykonywane przez innych może się bardzo różnić w Twoim systemie.
Slai

Odpowiedzi:

174

Containswywołania IndexOf:

public bool Contains(string value)
{
    return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
}

Które wywołania CompareInfo.IndexOf, które ostatecznie używają implementacji CLR.

Jeśli chcesz zobaczyć, jak ciągi są porównywane w środowisku CLR, to pokaże Ci (poszukaj CaseInsensitiveCompHelper ).

IndexOf(string)nie ma opcji i Contains()używa porównania porządkowego (porównanie bajt po bajcie, zamiast próbować przeprowadzić inteligentne porównanie, na przykład e z é).

Więc IndexOfbędzie nieznacznie szybszy (w teorii), ponieważ IndexOfprzechodzi bezpośrednio do wyszukiwania ciągów za pomocą FindNLSString z kernel32.dll (moc reflektora!).

Zaktualizowano dla .NET 4.0 - IndexOf nie używa już porządkowego porównania, więc zawartość może być szybsza. Zobacz komentarz poniżej.

Chris S.
źródło
3
Ta odpowiedź nie jest prawie poprawna, po prostu spójrz tutaj stackoverflow.com/posts/498880/revisions, aby uzyskać wyjaśnienie
pzaj
55
Moja odpowiedź ma 7 lat i jest oparta na frameworku .NET 2. Wersja 4 IndexOf()rzeczywiście używa StringComparison.CurrentCulturei Contains()używa, StringComparison.Ordinalktóre będą szybsze. Ale tak naprawdę różnice w szybkości, o których mówimy, są niewielkie - chodzi o to, że jedno wywołuje drugie, a zawartość jest bardziej czytelna, jeśli nie potrzebujesz indeksu. Innymi słowy, nie martw się o to.
Chris S
21

Prawdopodobnie nie będzie to miało żadnego znaczenia. Przeczytaj ten post na Coding Horror;): http://www.codinghorror.com/blog/archives/001218.html

Gonzalo Quero
źródło
4
Podciągamy się do szefa, czy my ...? : D Masz jednak rację, w porównaniu z czasem potrzebnym do obsłużenia żądania http, jednokrotne przeszukanie krótkiego ciągu nie ma znaczenia.
Fowl
Bardzo zabawna lektura, ale denerwuje mnie jego początkowa skarga dotycząca konkatenacji to zużycie pamięci, wtedy testuje tylko czas spędzony na różnych sposobach łączenia łańcuchów.
sab669
11

Contains (s2) jest wiele razy (na moim komputerze 10 razy) szybsze niż IndexOf (s2), ponieważ Contains używa StringComparison.Ordinal, które jest szybsze niż wyszukiwanie uwzględniające kulturę, które domyślnie wykonuje IndexOf (ale może się zmienić w .net 4.0 http: //davesbox.com/archive/2008/11/12/breaking-changes-to-the-string-class.aspx ).

Contains ma dokładnie taką samą wydajność jak IndexOf (s2, StringComparison.Ordinal)> = 0 w moich testach, ale jest krótszy i jasno określa twój zamiar.

ggf31416
źródło
2
Zmiany w .NET 4.0 zostały najwyraźniej cofnięte przed przejściem na RTM, więc nie polegałbym zbytnio na tym artykule blogs.msdn.com/bclteam/archive/2008/11/04/ ...
Stephen Kennedy
7

Prowadzę prawdziwy przypadek (w przeciwieństwie do syntetycznego benchmarku)

 if("=,<=,=>,<>,<,>,!=,==,".IndexOf(tmps)>=0) {

przeciw

 if("=,<=,=>,<>,<,>,!=,==,".Contains(tmps)) {

Jest to istotna część mojego systemu i jest wykonywana 131 953 razy (dzięki DotTrace).

Jakkolwiek szokująca niespodzianka , wynik jest odwrotny od oczekiwanego

  • Indeks 533 ms.
  • Zawiera 266 ms.

: - /

net framework 4.0 (aktualizacja na dzień 13-02-2012)

magallanes
źródło
1
ponieważ INTjest znacznie większy niż BOOLi IndexOf>=0powoduje jeszcze jeden krok
Eric Yin
3
Zapomniałeś użyć ´StringComparison.Ordinal´
Davi Fiamenghi
6

Używając Reflectora, możesz zobaczyć, że Contains jest zaimplementowany przy użyciu IndexOf. Oto implementacja.

public bool Contains(string value)
{
   return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
}

Więc Contains jest prawdopodobnie trochę wolniejsze niż bezpośrednie wywołanie IndexOf, ale wątpię, czy będzie miało to jakiekolwiek znaczenie dla rzeczywistej wydajności.

Brian Rasmussen
źródło
1
Tak, ale aby użyć indexof jako bool, musiałby wykonać porównanie poza funkcją. To najprawdopodobniej dałoby taki sam wynik jak Contains, prawda?
Gonzalo Quero
1
Prawdopodobnie, ale zapisujesz jedno wywołanie metody (chyba że można je wstawić). Jak powiedziałem, prawdopodobnie nigdy nie będzie to znaczące.
Brian Rasmussen
6

Jeśli naprawdę chcesz mikrooptymalizować swój kod, najlepszym podejściem jest zawsze benchmarking.

Framework .net ma doskonałą implementację stopera - System.Diagnostics.Stopwatch

Andrew Harry
źródło
To najlepsze rozwiązanie, ale jeśli chcesz uzyskać szybkie podejście, po prostu naciśnij przycisk pauzy w sesji debugowania. Kontrola kodu prawdopodobnie zatrzyma się w najwolniejszej części mniej więcej w 50% przypadków .
Jeremy Thompson
@JeremyThompson powtórz metodę „pause debug” jak 10 razy i masz sobie profilera
Xeuron
4

Z krótkiego czytania wynika, że ​​pod maską metoda String.Contains po prostu wywołuje String.IndexOf. Różnica polega na tym, że String.Contains zwraca wartość logiczną, podczas gdy String.IndexOf zwraca liczbę całkowitą, w której (-1) oznacza, że ​​podłańcuch nie został znaleziony.

Proponuję napisać mały test z około 100 000 iteracjami i przekonaj się sam. Gdybym miał zgadywać, powiedziałbym, że IndexOf może być nieco szybszy, ale tak jak powiedziałem, to tylko przypuszczenie.

Jeff Atwood ma na swoim blogu dobry artykuł o sznurkach . Chodzi bardziej o konkatenację, ale może być pomocna.

Mike Roosa
źródło
3

Tak jak aktualizacja tego, przeprowadziłem testy i podałem, że ciąg wejściowy jest dość duży, a równoległe Regex jest najszybszą metodą C #, jaką znalazłem (pod warunkiem, że masz więcej niż jeden rdzeń, jaki sobie wyobrażam)

Na przykład uzyskanie całkowitej liczby dopasowań -

needles.AsParallel ( ).Sum ( l => Regex.IsMatch ( haystack , Regex.Escape ( l ) ) ? 1 : 0 );

Mam nadzieję że to pomoże!

Gary
źródło
1
Cześć phild w osobnym wątku zaktualizował to o wersję z tomasp.net/articles/ahocorasick.aspx, która, pod warunkiem, że Twoje słowa kluczowe (igły) nie zmieniają się, jest znacznie szybsza.
gary
2

Skorzystaj z biblioteki wzorcowej, takiej jak ta niedawna wyprawa Jona Skeeta, aby to zmierzyć.

Caveat Emptor

Podobnie jak w przypadku wszystkich (mikro) pytań dotyczących wydajności, zależy to od wersji używanego oprogramowania, szczegółów sprawdzanych danych i kodu otaczającego wywołanie.

Jak w przypadku wszystkich pytań dotyczących (mikro) wydajności, pierwszym krokiem musi być uzyskanie działającej wersji, która będzie łatwa w utrzymaniu. Następnie zamiast zgadywania można zastosować benchmarking, profilowanie i dostrajanie do zmierzonych wąskich gardeł.

David Schmitt
źródło
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
Mike Stockdale
Biblioteka połączona jest tylko jedną z wielu i nie jest głównym celem odpowiedzi. Nie sądzę, aby zamieszczenie źródła lub opisu bibliotek poprawiłoby odpowiedź, tę stronę czy świat.
David Schmitt
3
-1; pytanie brzmiało: "Czy ktoś wie, czy metoda String.Contains działa lepiej niż metoda String.IndexOf?" - Twoja odpowiedź brzmi „użyj biblioteki wzorcowej”, co zasadniczo oznacza „nie wiem, zrób to sam”, „to zależy”, co oznacza „nie wiem” i „uzyskaj działającą wersję i profil” , co oznacza również „nie wiem, zrób to sam”. To nie jest „Jeopardy” - należy podać odpowiedź na zadawane pytanie , a nie jak do idei - ich miejsce jest w komentarzach .
-7

Dla każdego, kto nadal to czyta, indexOf () prawdopodobnie będzie działać lepiej w większości systemów korporacyjnych, ponieważ funkcja include () nie jest kompatybilna z IE!

Zargontapel
źródło
12
wrzuć nowy OutOfScopeException ();
Raphaël