2010-08-13 14 views
9

Wykonywanie podciągów ciągu znaków jest bardzo powszechną operacją manipulacji ciągami, ale słyszałem, że mogą występować znaczne różnice w wydajności/implementacji między platformą Java i .NET. W szczególności słyszałem, że w Javie java.lang.String oferuje stałą operacji czasowej dla substring, ale w .NET, System.String oferuje liniowy wydajność Substring.Porównanie wydajności operacji podciągu między .NET i Java

Czy rzeczywiście tak jest? Czy można to potwierdzić w dokumentacji/kodzie źródłowym itp.? Czy ta implementacja jest specyficzna, czy określona przez język i/lub platformę? Jakie są plusy i minusy każdego podejścia? Co powinien przejść użytkownik przechodzący z jednej platformy na drugą, aby uniknąć wpadnięcia w pułapki wydajności?

+1

Dlaczego nie uruchamiać własnych mikro testów porównawczych, aby przetestować to ? Czy możesz linkować do źródeł, które mówią, że ma "złą" wydajność? – Oded

+0

@Oded: source to komentarz Danny Chena tutaj http://stackoverflow.com/questions/3474254/how-to-make-a-first-letter-capital-in-c/3474263#3474263; Szczerze mówiąc, byłbym zdziwiony, gdyby 'Substring' nie był operacją" O (1) 'czasu i przestrzeni (jak np. Java), ale daję mu korzyść z wątpliwości, ponieważ nie znam .NET. – polygenelubricants

+1

Co to znaczy "zła skuteczność"? W stosunku do czego? .NET ma również słabą wydajność, na przykład w porównaniu z C++. Czy powinniśmy z tego zrezygnować .NET? –

Odpowiedz

11

W .NET Substring wynosi O (n) zamiast O (1) Java. Dzieje się tak dlatego, że w .NET obiekt String zawiera wszystkie rzeczywiste same dane znakowe: - więc pobranie podciągu wymaga skopiowania wszystkich danych w nowym podłańcuchu. W języku Java, substring może po prostu utworzyć nowy obiekt, odwołując się do oryginalnej tablicy znaków, z innym początkowym indeksem i długością.

Są plusy i minusy każdego podejścia:

  • podejście .NET zawiera lepszej spójności pamięci podręcznej, stwarza mniejsze obiekty , a unika się sytuacji, w której jedna mała podciąg zapobiega bardzo dużą char[] będąc garbage zebrane . Wierzę, że w niektórych przypadkach może to również ułatwić wewnętrzną interopcję.
  • podejście Javy ułatwia podejmowanie podciąg bardzo wydajny, a prawdopodobnie niektóre inne operacje zbyt

Jest trochę bardziej szczegółowo w moim strings article.

Jeśli chodzi o ogólną kwestię unikania pułapek wydajności, myślę, że powinienem mieć gotową odpowiedź gotową do wycinania i wklejania: upewnij się, że Twoja architektura jest wydajna i implementuj ją w najbardziej czytelny sposób. Zmierz wydajność i zoptymalizuj miejsce, w którym znajdują się wąskie gardła.


Nawiasem mówiąc, dzięki temu string wyjątkowy - to nie tylko typ matrycowy, którego wykorzystanie pamięci zależy od przykład w tym samym CLR.

Dla małych strun to duża wygrana. Wystarczająco złe jest to, że istnieje cały koszt związany z obiektem , ale w przypadku, gdy w grę wchodzi także dodatkowa tablica, ciąg znaków jednoznakowych może zająć około 36 bajtów w Javie. (To jest liczba "palców w powietrzu" - nie pamiętam dokładnych kosztów obiektu, zależy to również od używanej maszyny wirtualnej.)

2

Korzystanie reflektor jest to, co można uzyskać z podciąg (Int32, Int32)

[SecuritySafeCritical, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] 
public string Substring(int startIndex, int length) 
{ 
    return this.InternalSubStringWithChecks(startIndex, length, false); 
} 

jeśli dalej dzieje wewnątrz ostatniej rozmowy jest do

internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount) 

, który kopiuje znaki za pomocą wskaźników. Kompletny kod wygląda na duży, ale nie zobaczysz, jak szybko lub wolno, dopóki go nie uruchomisz i nie przetestujesz go.

0

To naprawdę zależy od obciążenia pracą. Jeśli używasz pętli i wykonujesz wiele wywołań podciąganych, możesz mieć problem. W przypadku postu SO, do którego się odnosisz, wątpię, czy kiedykolwiek byłby to problem. Przy takim nastawieniu można jednak zawsze skończyć w sytuacji "śmierci przez tysiąc papierków". W SO zakładać ty patrz, mamy następujący:

String after = before.Substring(0, 1).ToUpper() + before.Substring(1); 

Zakładając, że kompilator nie zrobić kilka szalonych optymalizacje, to stworzy co najmniej cztery nowe ciągi (2 Substring połączeń, a ToUpper połączeniami i powiązanie). Podłańcuch jest zaimplementowany dokładnie tak, jak się spodziewałeś (kopia napisowa), ale trzy z powyższych napisów szybko staną się śmieciami. Zrobienie dużej ilości tego spowoduje niepotrzebne ciśnienie w pamięci. Mówię "niepotrzebnie", ponieważ prawdopodobnie można zaproponować bardziej ekonomiczne rozwiązanie, które wymaga jedynie nieco więcej czasu.

W końcu, profiler jest twoim najlepszym przyjacielem :)

Powiązane problemy