O ile szybszy jest C ++ niż C #?


245

Czy jest teraz na odwrót?

Z tego, co słyszałem, jest kilka obszarów, w których C # okazuje się być szybszy niż C ++, ale nigdy nie miałem odwagi przetestować go samodzielnie.

Pomyślałem, że każdy z was może szczegółowo wyjaśnić te różnice lub wskazać mi właściwe miejsce na informacje na ten temat.


7
Chronione, aby zapobiec publikowaniu kolejnych losowych testów porównawczych. Jeśli uważasz, że możesz zrobić swoją sprawę, będziesz potrzebować 10 powtórzeń.
Robert Harvey

2
Jak to w ogóle nie jest zamknięte jako opinia / argumentacja? Czy nadal nie korzystam z StackOverflow? (Nie sugeruję zamknięcia, po prostu jestem ciekawy. Uwielbiam pytania, które pobudzają argumentację)
Bill K

1
To prawie sporne pytanie, biorąc pod uwagę, że żyjemy w czasach, w których IL można przekształcić w CPP i zoptymalizować z tego miejsca: docs.unity3d.com/Manual/IL2CPP.html
pixelpax

Język, który sprawdza dostęp do tablicy poza zakresem, nigdy nie będzie lepszy niż język, który go nie ma.
Seva Alekseyev

@SevaAlekseyev To nie jest język, ale kompilator. Jednym z powodów, dla których C ++ jest tak szybki (poza oczywistymi), jest to, że kompilatory C ++ istnieją już od 35 lat (jeśli nie więcej). Nic nie stoi na przeszkodzie, aby kompilatory C # poprawiały się z czasem. Dla wspomnianego przypadku przeczytaj ten stackoverflow.com/questions/16713076/...
Trap

Odpowiedzi:


342

Nie ma ścisłego powodu, dla którego język oparty na bajtach, taki jak C # lub Java, który ma JIT, nie może być tak szybki jak kod C ++. Jednak kod C ++ był przez długi czas znacznie szybszy, a także dzisiaj jest w wielu przypadkach. Wynika to głównie z tego, że bardziej zaawansowane są optymalizacje JIT, których wdrożenie jest skomplikowane, a te naprawdę fajne pojawiają się dopiero teraz.

Więc C ++ jest szybszy, w wielu przypadkach. Ale to tylko część odpowiedzi. Przypadki, w których C ++ jest rzeczywiście szybszy, to wysoce zoptymalizowane programy, w których eksperci programiści gruntownie zoptymalizowali kod. Jest to nie tylko bardzo czasochłonne (a przez to kosztowne), ale często prowadzi do błędów z powodu nadmiernej optymalizacji.

Z drugiej strony, kod w tłumaczonych językach przyspiesza w późniejszych wersjach środowiska uruchomieniowego (.NET CLR lub Java VM), bez żadnego działania. Istnieje wiele przydatnych optymalizacji kompilatorów JIT, które są po prostu niemożliwe w językach ze wskaźnikami. Ponadto niektórzy twierdzą, że zbieranie śmieci powinno być na ogół tak szybkie lub szybsze jak ręczne zarządzanie pamięcią, aw wielu przypadkach tak jest. Możesz to wszystko zaimplementować i osiągnąć w C ++ lub C, ale będzie to znacznie bardziej skomplikowane i podatne na błędy.

Jak powiedział Donald Knuth, „przedwczesna optymalizacja jest źródłem wszelkiego zła”. Jeśli naprawdę wiesz na pewno, że twoja aplikacja będzie się składać głównie z arytmetyki krytycznej pod względem wydajności i że będzie wąskim gardłem, a na pewno będzie szybsza w C ++, i masz pewność, że C ++ nie będzie kolidować z innymi wymagania, przejdź do C ++. W każdym innym przypadku skoncentruj się na prawidłowym wdrożeniu aplikacji w języku, który najbardziej Ci odpowiada, a następnie znajdź wąskie gardła wydajności, jeśli działa ona zbyt wolno, a następnie pomyśl o tym, jak zoptymalizować kod. W najgorszym przypadku może być konieczne wywołanie kodu C za pośrednictwem obcego interfejsu funkcji, więc nadal będziesz mieć możliwość pisania krytycznych części w języku niższego poziomu.

Pamiętaj, że stosunkowo łatwo jest zoptymalizować poprawny program, ale znacznie trudniej jest poprawić zoptymalizowany program.

Podanie rzeczywistych procentowych korzyści prędkości jest niemożliwe, w dużej mierze zależy od twojego kodu. W wielu przypadkach implementacja języka programowania nie jest nawet wąskim gardłem. Przyjmij testy porównawcze na stronie http://benchmarksgame.alioth.debian.org/ z dużym sceptycyzmem, ponieważ w dużej mierze testują one kod arytmetyczny, który najprawdopodobniej wcale nie jest podobny do twojego kodu.


92
<quote> kod w interpretowanych językach staje się szybszy w późniejszych wersjach środowiska wykonawczego </quote> Ponieważ kod skompilowany przez lepszą wersję kompilatora będzie również szybszy.
Martin York,

47
W rzeczywistości istnieje co najmniej jeden powód: JIT musi być szybki i nie może sobie pozwolić na poświęcenie czasu na różne zaawansowane optymalizacje dostępne dla kompilatora C ++.
Nemanja Trifunovic

63
„ale również często prowadzi do błędów z powodu nadmiernej optymalizacji”. [cytat rozpaczliwie potrzebny]. Pracuję w krajowym laboratorium i optymalizujemy nasz kod. Zwykle nie powoduje to błędów w kodzie.
Todd Gamblin,

35
„Optymalizacja prawidłowego programu jest stosunkowo łatwa, ale o wiele trudniej jest poprawić zoptymalizowany program”.
gradbot

20
Inge: nie jestem pewien, czy jesteś na dobrej drodze. Tak, język C # jest zaimplementowany w innym języku, ale kompilator JIT generuje kod maszynowy, więc nie jest to język interpretowany. W związku z tym nie jest ograniczony przez implementację C ++. Nie jestem pewien, dlaczego uważasz, że dodanie menedżera do czegoś z natury przyspiesza.
Martin Probst

202

C # może nie być szybszy, ale sprawia, że ​​TY / MNIE szybciej. To najważniejsza miara tego, co robię. :)


68
Haha, jest dobry cytat Larry'ego Walla na ten temat. Mówi o perlu, ale można o tym pomyśleć we wszystkich dyskusjach dotyczących języków i wydajności: „... wcześniejsze języki komputerowe, takie jak Fortran i C, zostały zaprojektowane w celu efektywnego wykorzystania kosztownego sprzętu komputerowego. W przeciwieństwie do tego, Perl jest zaprojektowany do efektywnie wykorzystuj drogich programistów komputerowych ”
Falaina,

60
1. „C # jest znacznie szybszy niż C ++” 2. „To nie może być prawda” 1. „Pewnie, że może” 2. „O ile?” 1. „Zwykle o 3-4 miesiące”
Dmitry S.

2
dla C ++, który naprawdę zależy od używanych bibliotek, C # nie jest zazwyczaj szybszy, .NET to, kiedy mówisz o wydajności
Ion Todirel

To ten sam powód, dla którego możesz użyć Pythona zamiast C do napisania kodu ... ale po utworzeniu ciężkich obliczeń możesz poczuć różnicę w wydajności.
Ch3shire,

To zależy od tego, do czego jesteś przyzwyczajony. Programuję w C ++ znacznie szybciej niż w C #. Znajomość biblioteki jest dużą jej częścią, ponieważ nie chcesz wymyślać koła dla podstawowych zadań. Głównym problemem C / C ++ było zarządzanie wskaźnikami, które jest prawie rozwiązane dzięki inteligentnym wskaźnikom. Powiedziawszy, że C ++ poważnie brakuje obszernej biblioteki, jaką oferuje .NET i Java, co może znacznie przyspieszyć rozwój. Nie zostanie to wkrótce rozwiązane, ponieważ ci std chłopcy lubią spędzać czas na ulepszaniu szablonów zamiast rozszerzeń biblioteki.
gast128

87

Jest pięć pomarańczy szybciej. Lub raczej: nie może być (poprawnej) ogólnej odpowiedzi. C ++ jest językiem kompilowanym statycznie (ale jest też optymalizacja kierowana profilem), C # działa wspomagany przez kompilator JIT. Jest tak wiele różnic, że na pytania typu „o ile szybciej” nie można odpowiedzieć, nawet przez podanie rzędów wielkości.


177
Czy masz jakieś dowody na poparcie twojego oburzającego roszczenia pięciu pomarańczy? Moje eksperymenty wskazują najwyżej 2 pomarańcze, z 3 poprawą mango podczas metaprogramowania szablonu.
Alex

42
W drożdżach nie milczy, to o wiele szybciej.
Chris

11
Z mojego doświadczenia wynika, że ​​jest to raczej 5,2 pomarańczy. Ale to zależy od używanego owocomierza.
Dio F

4
Aktualizacja, StackOverflow sam pomieszał się i obsługuje komentarze nieefektywne, a tym samym mniej bananów (300 bananów gorszych niż powinno być): meta.stackexchange.com/questions/254534/…
KeksArmee

87

Zacznę od tego, że nie zgadzam się z częścią zaakceptowanej (i dobrze ocenionej) odpowiedzi na to pytanie, stwierdzając:

Istnieje wiele powodów, dla których kod JITted będzie działał wolniej niż odpowiednio zoptymalizowany program C ++ (lub inny język bez narzutów), w tym:

  • cykle obliczeniowe spędzone na kodzie JITting w czasie wykonywania są z definicji niedostępne do użycia podczas wykonywania programu.

  • wszelkie ścieżki dostępu w JITter będą konkurować z twoim kodem o instrukcje i pamięć podręczną danych w CPU. Wiemy, że pamięć podręczna dominuje pod względem wydajności, a języki rodzime, takie jak C ++, z definicji nie mają tego rodzaju rywalizacji.

  • budżet czasowy optymalizatora czasu działania jest z konieczności znacznie bardziej ograniczony niż budżet optymalizatora czasu kompilacji (jak zauważył inny komentator)

Konkluzja: W ostatecznym rozrachunku, to będzie prawie na pewno będzie w stanie stworzyć szybszą realizację w C ++ niż można w C # .

To powiedziawszy, o ile szybciej tak naprawdę nie da się kwantyfikować, ponieważ istnieje zbyt wiele zmiennych: zadanie, dziedzina problemów, sprzęt, jakość implementacji i wiele innych czynników. Przeprowadzisz testy swojego scenariusza, aby określić różnicę w wydajności, a następnie zdecydujesz, czy warto dodatkowy wysiłek i złożoność.

Jest to bardzo długi i złożony temat, ale uważam, że warto wspomnieć ze względu na kompletność, że optymalizator środowiska wykonawczego C # jest doskonały i jest w stanie przeprowadzić pewne dynamiczne optymalizacje w czasie wykonywania, które po prostu nie są dostępne dla C ++ ze względu na czas kompilacji ( statyczny) optymalizator. Mimo to przewaga jest zwykle głęboko w sądzie natywnej aplikacji, ale dynamiczny optymalizator jest przyczyną „ prawie podanego wyżej kwalifikatora pewno”.

-

Jeśli chodzi o względną wydajność, niepokoiły mnie również liczby i dyskusje, które widziałem w innych odpowiedziach, więc pomyślałem, że włączy się i jednocześnie zapewnię wsparcie dla oświadczeń, które wypowiedziałem powyżej.

Ogromną częścią problemu z tymi testami porównawczymi jest to, że nie możesz napisać kodu C ++ tak, jakbyś pisał w C # i spodziewał się uzyskania reprezentatywnych wyników (np. Wykonanie tysięcy alokacji pamięci w C ++ da ci straszne liczby).

Zamiast tego napisałem nieco więcej idiomatycznego kodu C ++ i porównałem go z podanym kodem C # @Wiory. Dwie główne zmiany, które wprowadziłem w kodzie C ++ to:

1) używany wektor :: Reserve ()

2) spłaszczono tablicę 2d do 1d, aby uzyskać lepszą lokalizację pamięci podręcznej (ciągły blok)

C # (.NET 4.6.1)

private static void TestArray()
{
    const int rows = 5000;
    const int columns = 9000;
    DateTime t1 = System.DateTime.Now;
    double[][] arr = new double[rows][];
    for (int i = 0; i < rows; i++)
        arr[i] = new double[columns];
    DateTime t2 = System.DateTime.Now;

    Console.WriteLine(t2 - t1);

    t1 = System.DateTime.Now;
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < columns; j++)
            arr[i][j] = i;
    t2 = System.DateTime.Now;

    Console.WriteLine(t2 - t1);
}

Czas pracy (Release): Init: 124ms, Fill: 165ms

C ++ 14 (Clang v3.8 / C2)

#include <iostream>
#include <vector>

auto TestSuite::ColMajorArray()
{
    constexpr size_t ROWS = 5000;
    constexpr size_t COLS = 9000;

    auto initStart = std::chrono::steady_clock::now();

    auto arr = std::vector<double>();
    arr.reserve(ROWS * COLS);

    auto initFinish = std::chrono::steady_clock::now();
    auto initTime = std::chrono::duration_cast<std::chrono::microseconds>(initFinish - initStart);

    auto fillStart = std::chrono::steady_clock::now();

    for(auto i = 0, r = 0; r < ROWS; ++r)
    {
        for (auto c = 0; c < COLS; ++c)
        {
            arr[i++] = static_cast<double>(r * c);
        }
    }

    auto fillFinish = std::chrono::steady_clock::now();
    auto fillTime = std::chrono::duration_cast<std::chrono::milliseconds>(fillFinish - fillStart);

    return std::make_pair(initTime, fillTime);
}

Czas pracy (wydanie): Init: 398µs (tak, to mikrosekundy), Fill: 152ms

Całkowity czas pracy: C #: 289ms, C ++ 152ms (około 90% szybciej)

Spostrzeżenia

  • Zmiana implementacji C # na tę samą implementację tablicy 1d dała Init: 40ms, Fill: 171ms, Total: 211ms ( C ++ był wciąż prawie 40% szybszy ).

  • Znacznie trudniej jest zaprojektować i napisać „szybki” kod w C ++ niż napisać „zwykły” kod w dowolnym języku.

  • Zadziwiająco łatwo jest uzyskać słabą wydajność w C ++; widzieliśmy to przy niezastrzeżonej wydajności wektorów. I jest wiele takich pułapek.

  • Wydajność C # jest raczej niesamowita, jeśli wziąć pod uwagę wszystko, co dzieje się w czasie wykonywania. Ta wydajność jest stosunkowo łatwo dostępna.

  • Więcej niepotwierdzonych danych porównujących wydajność C ++ i C #: https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=gpp&lang2=csharpcore

Najważniejsze jest to, że C ++ daje znacznie większą kontrolę nad wydajnością. Czy chcesz użyć wskaźnika? Referencja? Pamięć stosu? Sterta? Dynamiczny polimorfizm czy wyeliminować obciążenie środowiska wykonawczego vtable ze statycznym polimorfizmem (poprzez szablony / CRTP)? W C ++ musisz ... er, dostać się dołożenia wszelkich tych wyborów (i więcej) samemu, najlepiej tak, że najlepsze rozwiązanie odnosi się do problemu jesteś Walka.

Zadaj sobie pytanie, czy naprawdę chcesz lub potrzebujesz tej kontroli, ponieważ nawet w powyższym trywialnym przykładzie widać, że chociaż istnieje znaczna poprawa wydajności, dostęp do niej wymaga głębszej inwestycji.


16
@Quonux dziękuję za komentarz. Oczywiście nie jest to „prawdziwy program”. Istotą testów porównawczych był refaktoryzacja testu porównawczego C # oferowanego gdzie indziej na tej stronie jako dowód, że kod JITted jest w jakiś sposób szybszy niż natywny - nie jest, a test porównawczy potencjalnie wprowadzał w błąd nowych ludzi.
U007D,

9
@Quonux, dlaczego musisz tak pisać? To ludzie tacy jak ty sprawiają, że nie lubię przepełnienia stosu.
Markus Knappen Johansson

5
@MarkusKnappenJohansson Miałem zły dzień;), jestem też człowiekiem, usunąłem swoje zdanie, ale moja opinia wciąż obowiązuje. Och, proszę, nie lubię SO tylko dlatego, że są tacy „głupi” ludzie :). Miłego.
Quonux,

9
ZUPEŁNIE NIEPRZERWANY BENCHMARK. W wersji C ++ po prostu rezerwujesz część pamięci (a potem zastanawiasz się, jak wykonanie tej operacji zajmuje mikrosekundy). W wersji C # tworzysz 5000 tablic (tworzenie instancji obiektów w pamięci). C ++ jest szybszy niż C # ... ale różnica nie jest bliska 40% ... w tej chwili jest bardziej w zakresie <10%. Przykład ilustruje to, że programiści powinni trzymać się wybranego przez siebie języka (a z twojego profilu widać, że jesteś programistą C ++). W języku C # możesz wykonać tablicę 2D int[,]... podążając za przykładem.
nikib3ro

3
Z tego co mogę powiedzieć, kod w twoim przykładzie C ++ dosłownie po prostu alokuje pamięć przed czasem. Implementacja PROPER C # napisałaby po prostu „List <double> arrs = new List <double> (ROWS * COLS)”, który przydziela pamięć wymaganą do indeksowania tablicy 2-wymiarowej w formacie 1-wymiarowym (np. Co zrobiłeś w C ++). Absolutnie nie ma powodu, aby przydzielać tablicę dwuwymiarową i ręcznie ją spłaszczyć - ogromna liczba iteracji we wstępnym teście jest przyczyną cholernej wydajności. Wyobrażam sobie, że narzut nadal byłby większy w C #, ale nie w znacznej ilości.
JDSweetBeat

62

Z mojego doświadczenia (i dużo pracowałem w obu językach), głównym problemem w C # w porównaniu do C ++ jest wysokie zużycie pamięci i nie znalazłem dobrego sposobu na kontrolowanie tego. To zużycie pamięci ostatecznie spowolniło oprogramowanie .NET.

Innym czynnikiem jest to, że kompilator JIT nie może sobie pozwolić na zbyt wiele czasu na zaawansowane optymalizacje, ponieważ działa w czasie wykonywania, a użytkownik końcowy zauważyłby go, jeśli zajmie to zbyt dużo czasu. Z drugiej strony, kompilator C ++ ma cały czas potrzebny na optymalizacje w czasie kompilacji. Ten czynnik jest znacznie mniej istotny niż zużycie pamięci, IMHO.


6
W jednym z prac w projekcie musieliśmy wydobyć olbrzymie ilości danych, w tym jednocześnie przechowywać wiele GB w pamięci i wykonywać na nich drogie obliczenia - wymagało to precyzyjnej kontroli wszystkich przydziałów, C ++ był właściwie jedynym wyborem. +1 dla C ++. Z drugiej strony, był to tylko jeden projekt, spędziliśmy większość czasu na pisaniu systemów, które współdziałały z powolnymi symulatorami, a debugowanie może być koszmarem, więc żałuję, że nie mogliśmy użyć języka optymalizującego czas programisty dla wszystkich innych rzeczy.
Bogatyr

7
@IngeHenriksen: Jestem w pełni świadomy wzorca Dispose, ale to wcale nie pomaga w zarządzanej pamięci.
Nemanja Trifunovic

10
Pozbywanie się @IngeHenriksen zapewnia tylko, że wywołano metodę Dispose. Utylizacja nigdy nie zwalnia pamięci zbierającej śmieci. Metoda Dispose służy wyłącznie do czyszczenia niezarządzanych zasobów, takich jak uchwyty plików, i nie ma nic wspólnego z zarządzaniem pamięcią.
doug65536,

1
@NemanjaTrifunovic: „Kompilator JIT nie może sobie pozwolić na zbyt wiele czasu na zaawansowane optymalizacje”. Czy możesz przytoczyć niektóre optymalizacje, które nie są wykonywane przez JIT, ponieważ zajęłyby one zbyt dużo czasu?
Jon Harrop

5
@ user3800527: Nawet jeśli dodanie pamięci RAM było zawsze wykonalne (a nie jest - wyobraź sobie, że Microsoft dodaje pamięć RAM do każdego użytkownika MS Office), to nie rozwiąże problemu. Pamięć jest hierarchiczna, a program C # będzie miał znacznie więcej braków pamięci podręcznej niż C ++.
Nemanja Trifunovic

35

Jeden szczególny scenariusz, w którym C ++ nadal ma przewagę (i będzie, przez wiele lat) ma miejsce, gdy decyzje polimorficzne mogą być z góry określone w czasie kompilacji.

Ogólnie rzecz biorąc, enkapsulacja i odroczenie decyzji jest dobrą rzeczą, ponieważ sprawia, że ​​kod jest bardziej dynamiczny, łatwiejszy do dostosowania do zmieniających się wymagań i łatwiejszy w użyciu jako framework. Dlatego programowanie obiektowe w języku C # jest bardzo produktywne i można je uogólnić pod pojęciem „uogólnienie”. Niestety ten szczególny rodzaj uogólnienia wiąże się z pewnym kosztem w czasie wykonywania.

Zwykle koszt ten nie jest znaczny, ale istnieją aplikacje, w których narzut wirtualnych wywołań metod i tworzenia obiektów może mieć znaczenie (zwłaszcza, że ​​metody wirtualne uniemożliwiają inne optymalizacje, takie jak wstawianie wywołań metod). Tutaj C ++ ma ogromną przewagę, ponieważ można użyć szablonów, aby osiągnąć inny rodzaj uogólnienia, który nie ma wpływu na środowisko wykonawcze, ale niekoniecznie jest mniej polimorficzny niż OOP. W rzeczywistości wszystkie mechanizmy składające się na OOP można modelować przy użyciu jedynie technik szablonów i rozdzielczości kompilacji.

W takich przypadkach (i wprawdzie często są one ograniczone do specjalnych domen problemowych), C ++ wygrywa z C # i porównywalnymi językami.


6
W rzeczywistości maszyny wirtualne Java (i prawdopodobnie .NET) robią wszystko, aby uniknąć dynamicznej wysyłki. Zasadniczo, jeśli istnieje sposób na uniknięcie polimorfów, możesz być całkiem pewien, że twoja maszyna wirtualna to zrobi.
Martin Probst

3
+1 Zawsze mam problem z wyjaśnieniem tego moim kolegom z C #, którzy znają trochę C ++ w sposób, który pozwoliłby im docenić znaczenie. Wyjaśniłeś to całkiem ładnie.
Roman Starkov,

9
@crtracy: stawiasz zakład bez wysokowydajnych aplikacji komputerowych. Rozważ prognozowanie pogody, bioinformatykę i symulacje numeryczne. Przewaga wydajności C ++ w tych obszarach nie zmniejszy się, ponieważ żaden inny kod nie może osiągnąć porównywalnej wydajności na tym samym poziomie wysokiej abstrakcji.
Konrad Rudolph,

5
@Jon Jabłka i pomarańcze. Twoje konkretne twierdzenie brzmiało: „C # jest o rząd wielkości szybszy niż C ++ w kontekście metaprogramowania”, a nie „użycie wstępnie skompilowanego kodu jest o rząd wielkości szybsze niż kod interpretowany”. Chociaż jesteśmy przy tym, twoje twierdzenie, że generowanie kodu środowiska wykonawczego jest „bardziej ogólne” niż generowanie kodu w czasie kompilacji, jest również wyraźnie błędne - oba mają mocne i słabe strony. Generowanie kodu w czasie kompilacji wykorzystuje system typów do zapewnienia bezpieczeństwa typu statycznego - generowanie kodu środowiska wykonawczego nie może tego zrobić ( może zapewnić bezpieczeństwo typu silnego, ale nie bezpieczeństwo typu statycznego ).
Konrad Rudolph

5
@ user3800527 Myślę, że brakuje Ci sedna tej odpowiedzi. Oczywiście możesz obejść ten problem, przerywając enkapsulację i schodząc do struktur niskiego poziomu - możesz pisać asemblery w (większości) dowolnym języku. To, co sprawia, że ​​C ++ jest (prawie) wyjątkowy i wyjątkowo nadaje się do programowania o wysokiej wydajności, polega na tym, że można tworzyć abstrakcje na wysokim poziomie , które nie wymagają żadnych opłat za środowisko uruchomieniowe. Nie musisz więc pisać kodu w języku asemblera w C ++, aby uzyskać najwyższą wydajność: dobrze napisany sort(arr, generic_comparer)będzie tak samo wydajny jak ręcznie napisana pętla w C ++. Nigdy nie będzie w języku C #.
Konrad Rudolph

20

C ++ (w tym przypadku C) daje dokładną kontrolę nad strukturami danych. Jeśli chcesz coś zmienić, masz tę opcję. Duże zarządzane aplikacje Java lub .NET (OWB, Visual Studio 2005 ) korzystające z wewnętrznych struktur danych bibliotek Java / .NET niosą ze sobą bagaż. Widziałem, jak sesje projektantów OWB używają ponad 400 MB pamięci RAM i BIDS do projektowania kostek lub ETL również w setkach MB.

Przy przewidywalnym obciążeniu (takim jak większość testów porównawczych, które powtarzają proces wiele razy), JIT może zapewnić ci kod zoptymalizowany na tyle dobrze, że nie ma praktycznej różnicy.

W przypadku dużych aplikacji IMO różnica polega nie tyle na JIT, ile na strukturach danych, z których korzysta sam kod. Tam, gdzie aplikacja wymaga dużej ilości pamięci, użycie pamięci podręcznej będzie mniej wydajne. Brakujące pamięci podręczne w nowoczesnych procesorach są dość drogie. Tam, gdzie C lub C ++ naprawdę wygrywają, możesz zoptymalizować wykorzystanie struktur danych, aby ładnie grać z pamięcią podręczną procesora.


19

W przypadku grafiki standardowa klasa grafiki C # jest znacznie wolniejsza niż GDI, do którego można uzyskać dostęp za pomocą C / C ++. Wiem, że nie ma to nic wspólnego z samym językiem, a bardziej z całkowitą platformą .NET, ale grafikę oferuje deweloperowi jako zamiennik GDI, a jego wydajność jest tak zła, że ​​nie odważyłbym się nawet zrobić grafiki z tym.

Mamy prosty test porównawczy, którego używamy, aby zobaczyć, jak szybka jest biblioteka graficzna, a to po prostu rysowanie losowych linii w oknie. C ++ / GDI wciąż jest niezadowolony z 10000 linii, podczas gdy C # / Graphics ma trudności z zrobieniem 1000 w czasie rzeczywistym.


5
Zaintrygowała mnie twoja odpowiedź. Czy przetestowałeś już ten sam test porównawczy z niebezpiecznym kodem i blokadami oraz samodzielnie rysujesz losowe linie? Teraz , że byłoby to ciekawe rzeczy do obejrzenia.
Pedery,

2
@Perydie nie, nie mam. po prostu używając GDI i .NET.Graphics na najbardziej podstawowe sposoby. co rozumiesz przez „samodzielne rysowanie losowych linii”?
QBziZ

1
Następnie powinieneś rozważyć przetestowanie tego, aby uzyskać bardziej realistyczne dane na temat szybkości C #. Oto ładny przegląd techniki: bobpowell.net/lockingbits.htm
Pedery

6
Nie chcemy tego robić, umieszczając osobne piksele w buforze klatek. Jeśli musisz sam wszystko zaimplementować, po co mieć interfejs API / platformę do kodowania? Dla mnie to nie jest argument. Nigdy nie musieliśmy umieszczać oddzielnych pikseli w buforze ramki w GDI do rysowania linii, i nie planujemy tego robić w .NET. Moim zdaniem użyliśmy realistycznych danych, a .NET okazał się powolny.
QBziZ

1
Cóż, mam tylko pojęcie, czym jest wykrywanie kropelek, ale samo stwierdzenie jednego pomiaru czasu raczej niczego nie dowodzi. Czy napisałeś już w C ++? W JavaScript? I w porównaniu do tych w C #? Poza tym nie sądzę, aby wykrywanie kropli używało wielu prymitywów graficznych. Popraw mnie, jeśli źle, ale myślę, że to algorytmy statystyczne wykonujące operacje na pikselach.
QBziZ

13

Odśmiecanie jest głównym powodem, dla którego Java # NIE MOŻE być używana w systemach czasu rzeczywistego.

  1. Kiedy odbędzie się GC?

  2. Jak długo to zajmie?

Jest to niedeterministyczne.


5
Nie jestem wielkim fanem Javy, ale nic nie mówi, że Java nie może używać GC przyjaznego w czasie rzeczywistym.
Zan Lynx

5
Istnieje wiele implementacji GC w czasie rzeczywistym, jeśli chcesz wyglądać. (GC jest obszar, który jest przepełniony z prac naukowych)
Arafangion

FWIW, Richard Jones właśnie opublikował zaktualizowaną wersję swojej książki na śmieci, która obejmuje między innymi najnowocześniejsze projekty GC w czasie rzeczywistym.
Jon Harrop,

11
To jest nonsensowny argument, Windows (i Linux) nie są systemami operacyjnymi czasu rzeczywistego. Twój kod C ++ może być w każdej chwili zamieniony na wiele 18-ms miejsc.
Henk Holterman

2
@HenkHolterman To prawda, ale zawsze można napisać moduł ładujący w zestawie, powiązać go z paskiem startowym jądra dla aplikacji i uruchomić aplikacje C ++ bezpośrednio na sprzęcie (w RT btw). Nie możesz tego zrobić w języku C #, a wszelkie wysiłki, które widziałem, naśladują tylko wstępnie skompilowany zestaw w języku C # i używają ton kodu C, co sprawia, że ​​używanie C # jest bezcelowe. Czytanie tego wszystkiego jest dość zabawne, ponieważ C # jest naprawdę bezużyteczny bez frameworku .NET.
zackery.fix

11

Musieliśmy ustalić, czy C # był porównywalny pod względem wydajności do C ++, i napisałem do tego kilka programów testowych (używając Visual Studio 2005 dla obu języków). Okazało się, że bez wyrzucania elementów bezużytecznych i tylko z uwzględnieniem języka (nie frameworka) C # ma w zasadzie taką samą wydajność jak C ++. Przydział pamięci jest znacznie szybszy w C # niż w C ++, a C # ma niewielką przewagę w determinizmie, gdy rozmiary danych są zwiększane poza granice linii pamięci podręcznej. Jednak wszystko to ostatecznie musiało zostać opłacone i istnieje ogromny koszt w postaci niedeterministycznych trafień wydajności dla C # z powodu wyrzucania elementów bezużytecznych.


1
W C ++ masz możliwość korzystania z różnych metod alokacji, więc w zależności od sposobu alokacji pamięci (AOT?) W C #, można to zrobić w ten sam sposób (ale znacznie szybciej) w C ++.
zackery.fix

5
@ zackery.fix .NET ma interesującą przewagę w alokacji sterty, ponieważ musi jedynie przesunąć wskaźnik, aby przydzielić nowy obiekt. Jest to możliwe tylko dzięki kompaktowaniu pojemnika na śmieci. Oczywiście możesz zrobić to samo w C ++, ale C ++ tego nie robi. To zabawne, jak używasz tego samego argumentu, aby powiedzieć „C # mógłby, ale nie robi, więc to śmieci” i „C ++ nie, ale mógł, więc jest niesamowity” :)
Luaan

9

Jak zwykle zależy to od aplikacji. Są przypadki, w których C # jest prawdopodobnie nieznacznie wolniejszy, i inne przypadki, w których C ++ jest 5 lub 10 razy szybszy, szczególnie w przypadkach, w których operacje można łatwo SIMD.


Najlepszym rozwiązaniem dla maszyn wirtualnych będzie kompilacja wygenerowanego kodu w czasie wykonywania (np. W celu dopasowania wyrażenia regularnego odczytanego w czasie wykonywania), ponieważ statycznie skompilowane waniliowe programy C ++ mogą korzystać z interpretacji, ponieważ nie mają wbudowanego kompilatora JIT.
Jon Harrop

Uwaga z przyszłości: .NET obsługuje SIMD i znajomych od około 2014 roku, choć nie jest powszechnie używany.
Luaan

9

Wiem, że to nie jest to, co zostało z prośbą, ale C # jest często szybciej pisać niż C ++, co jest dużą zaletą w otoczeniu komercyjnym.


2
Powiedziałbym, że przez większość czasu jest to szybsze :)
Trap

8

C / C ++ może działać znacznie lepiej w programach, w których występują albo duże tablice, albo ciężkie zapętlanie / iteracja nad tablicami (dowolnej wielkości). To jest powód, dla którego grafika jest ogólnie znacznie szybsza w C / C ++, ponieważ ciężkie operacje tablicowe leżą u podstaw prawie wszystkich operacji graficznych. .NET jest notorycznie powolny w operacjach indeksowania macierzy ze względu na wszystkie kontrole bezpieczeństwa, a jest to szczególnie prawdziwe w przypadku tablic wielowymiarowych (i tak, prostokątne tablice C # są nawet wolniejsze niż postrzępione tablice C #).

Premie C / C ++ są najbardziej widoczne, jeśli trzymasz się bezpośrednio wskaźników i unikasz wzmocnienia std::vectororaz innych pojemników wysokiego poziomu, a także inlinekażdej możliwej funkcji. W miarę możliwości używaj tablic oldschoolowych. Tak, będziesz potrzebować więcej wierszy kodu, aby osiągnąć to samo, co w Java lub C #, ponieważ unikasz kontenerów wysokiego poziomu. Jeśli potrzebujesz tablicy o dynamicznym rozmiarze, musisz tylko pamiętać o sparowaniunew T[] z odpowiednimdelete[] instrukcją (lub użyćstd::unique_ptr) - cena za dodatkową prędkość polega na tym, że musisz kodować ostrożniej. Ale w zamian możesz pozbyć się narzutu na zarządzaną pamięć / moduł wyrzucania elementów bezużytecznych, który może z łatwością wynosić 20% lub więcej czasu wykonywania mocno zorientowanych obiektowo programów w Javie i .NET, a także tych masowo zarządzanych koszty indeksowania macierzy pamięci. Aplikacje C ++ mogą również korzystać z niektórych fajnych przełączników kompilatora w określonych przypadkach.

Jestem programistą w językach C, C ++, Java i C #. Niedawno miałem rzadką okazję wdrożyć dokładnie ten sam program algorytmiczny w ostatnich 3 językach. Program miał wiele operacji matematycznych i wielowymiarowych. Mocno zoptymalizowałem to we wszystkich 3 językach. Wyniki były typowe dla tego, co zwykle widzę w mniej rygorystycznych porównaniach: Java była około 1,3 razy szybsza niż C # (większość JVM jest bardziej zoptymalizowana niż CLR), a wersja surowego wskaźnika C ++ była około 2,1 razy szybsza niż C #. Zauważ, że program C # używał tylko bezpiecznego kodu - moim zdaniem, równie dobrze możesz go napisać w C ++ przed użyciemunsafe słowa kluczowego.

Aby ktokolwiek pomyślał, że mam coś przeciwko C #, zakończę stwierdzeniem, że C # jest prawdopodobnie moim ulubionym językiem. Jest to najbardziej logiczny, intuicyjny i szybki język programowania, jaki do tej pory spotkałem. Wszystkie moje prototypy wykonuję w języku C #. Język C # ma wiele małych, subtelnych zalet w stosunku do Javy (tak, wiem, że Microsoft miał szansę naprawić wiele wad Javy, wchodząc do gry późno i prawdopodobnie kopiując Javę). Czy Calendarktoś wzniósł toast za klasę Javy ? Jeśli Microsoft kiedykolwiek poświęci prawdziwy wysiłek, aby zoptymalizować CLR i .NET JITter, C # może poważnie przejąć kontrolę. Jestem szczerze zaskoczony, że jeszcze tego nie zrobili - zrobili tak wiele rzeczy w języku C #, dlaczego nie pójść za tym z ciężkimi optymalizacjami kompilatora? Może jeśli wszyscy błagamy.


3
„musisz tylko pamiętać, aby sparować urządzenie new T[]z odpowiednim delete[] - Nie, nie robisz tego. Jest std::unique_ptrto dla ciebie.
emlai

zakładając, że napisałeś coś w grafice, po co pisać bezpieczny kod w języku c #, czy zastanawiałeś się nad użyciem niebezpiecznego kodu i porównujesz go ponownie?
user3800527 12.04.16

7

> Z tego co słyszałem ...

Wydaje się, że masz trudność w podjęciu decyzji, czy to, co usłyszałeś, jest wiarygodne, a trudność ta zostanie powtórzona podczas próby oceny odpowiedzi na tej stronie.

Jak zdecydujesz, czy to, co mówią ludzie, jest mniej lub bardziej wiarygodne niż to, co pierwotnie słyszałeś?

Jednym ze sposobów byłoby poprosić o dowód .

Gdy ktoś twierdzi, że „istnieją obszary, w których C # okazuje się być szybszy niż C ++”, zapytaj go, dlaczego tak mówi , poproś go o pokazanie pomiarów, poproś o pokazanie programów. Czasami po prostu popełniają błąd. Czasami dowiadujesz się, że po prostu wyrażają opinię, a nie dzielą się czymś, co mogą okazać się prawdą.

Często informacje i opinie będą mieszane w tym, co twierdzą ludzie, i będziesz musiał spróbować ustalić, który jest który. Na przykład z odpowiedzi na tym forum:

  • „Wykonaj testy na stronie http://shootout.alioth.debian.org/ z dużym sceptycyzmem, ponieważ w dużej mierze testują one kod arytmetyczny, który najprawdopodobniej wcale nie jest podobny do twojego kodu”.

    Zadaj sobie pytanie, czy naprawdę rozumiesz, co oznacza „te w dużej mierze testują kod arytmetyczny” , a następnie zadaj sobie pytanie, czy autor rzeczywiście wykazał, że jego twierdzenie jest prawdziwe.

  • „To raczej bezużyteczny test, ponieważ tak naprawdę zależy od tego, jak dobrze poszczególne programy zostały zoptymalizowane; Udało mi się przyspieszyć niektóre z nich o 4-6 razy lub więcej, co wyraźnie pokazuje, że porównanie między niezoptymalizowanymi programami jest raczej głupi."

    Zadaj sobie pytanie, czy autor rzeczywiście pokazał ci, że udało mu się „przyspieszyć niektóre z nich o 4-6 razy lub więcej” - łatwo to zgłosić!


Nie mogłem się z tobą bardziej zgodzić i dlatego zapytałem na tym forum ... W końcu odpowiedzi muszą gdzieś być, prawda? :)
Trap

1
Tak. Odpowiedź brzmi „to zależy”.
user49117,

6

W przypadku problemów „kłopotliwie równoległych” podczas korzystania z Intel TBB i OpenMP na C ++ zaobserwowałem około 10-krotny wzrost wydajności w porównaniu do podobnych problemów (czysta matematyka) z C # i TPL. SIMD jest jednym z obszarów, w którym C # nie może konkurować, ale mam również wrażenie, że TPL ma spore koszty ogólne.

To powiedziawszy, używam C ++ tylko do zadań krytycznych pod względem wydajności, o których wiem, że będę w stanie wielowątkowość i szybko uzyskać wyniki. W przypadku wszystkiego innego C # (i czasami F #) jest w porządku.


5

To niezwykle niejasne pytanie bez prawdziwych ostatecznych odpowiedzi.

Na przykład; Wolę grać w gry 3D tworzone w C ++ niż w C #, ponieważ wydajność jest zdecydowanie lepsza. (I znam XNA itp., Ale nie zbliża się to do rzeczywistości).

Z drugiej strony, jak wspomniano wcześniej; powinieneś rozwijać się w języku, który pozwala szybko robić to, co chcesz, a następnie w razie potrzeby optymalizować.


4
Czy możesz podać kilka przykładów? Gry napisane w C #, co znalazłeś powoli
Karl

1
Nawet przykładowe aplikacje dostarczone z instalacją wydawały się powolne.
David The Man,

9
Śmieciarka jest ogromną odpowiedzialnością za tworzenie gier w C #, ponieważ może kopać w dowolnym momencie, powodując poważne przerwy. Jawne zarządzanie pamięcią staje się łatwiejsze do tworzenia gier.
postfuturist

3
Większość współczesnych gier ma ograniczenia GPU. W przypadku takich gier nie ma znaczenia, czy logika (wykonywana na CPU) jest o 10% wolniejsza, nadal są one ograniczone przez GPU, a nie CPU. Śmieciarka to prawdziwy problem, powodujący przypadkowe krótkie zawieszanie się, jeśli przydziały pamięci nie są odpowiednio dostrojone.
Michael Entin

2
@postfuturist: To nie jest prawda na PC; śmieciarz robi tak dobrą robotę wchodząc i wychodząc, że nigdy nie miałem z tym żadnych problemów. Jednak na Xbox 360 i Zune / Windows-7-Phone, garbage collector nie jest prawie tak inteligentne jak na komputerze; Nigdy nie pisałem dla obu, ale ludzie, którzy powiedzieli mi, że śmieciarz to ogromny problem.
BlueRaja - Danny Pflughoeft

5

Języki .NET mogą być tak szybkie jak kod C ++, a nawet szybsze, ale kod C ++ będzie miał bardziej stałą przepustowość, ponieważ środowisko uruchomieniowe .NET musi wstrzymywać się z GC , nawet jeśli jest bardzo sprytne w kwestii swoich przerw.

Więc jeśli masz jakiś kod, który musi konsekwentnie działać szybko, bez żadnych przerw, .NET wprowadzi w pewnym momencie opóźnienie , nawet jeśli jesteś bardzo ostrożny z GC środowiska wykonawczego.


6
-1: To właściwie mit. Po pierwsze, opóźnienie idiomatycznego C ++ jest w rzeczywistości okropne i często znacznie gorsze niż .NET, ponieważ RAII powoduje lawiny niszczycieli, gdy duże struktury danych wypadają poza zasięg, podczas gdy współczesne GC są przyrostowe, a .NET jest nawet równoczesny. Po drugie, możesz całkowicie usunąć przerwy GC w .NET, nie przydzielając.
Jon Harrop,

2
Jeśli to zrobisz, musisz zrezygnować z używania BCL, ponieważ większość metod tworzy obiekty przejściowe.
Florian Doyon,

5
Jest to całkiem prawdą, dopiero w .net 4 GC stało się przyrostowe. Mamy dużą aplikację C #, która wstrzymuje się na kilka sekund dla GC. W przypadku aplikacji krytycznych pod względem wydajności jest to zabójca.
Justin

5
Istnieje powód, dla którego programy, które mają tendencję do wypychania sprzętu, zwykle używają C ++. Masz dokładniejszą kontrolę, kiedy jej potrzebujesz. Wydajność jest kluczowa tylko wtedy, gdy naciskasz system, w przeciwnym razie użyj C # lub Java, aby zaoszczędzić czas.
VoronoiPotato

4
jeśli nie możesz zarządzać zachowaniem pamięci podręcznej, nie możesz pobić zoptymalizowanego kodu c ++. Brak pamięci podręcznej od L1 do pamięci głównej może spowolnić działanie 100 razy.
DAG

4

Teoretycznie w przypadku długo działających aplikacji serwerowych język skompilowany w JIT może stać się znacznie szybszy niż w przypadku kompilacji natywnej. Ponieważ język kompilowany w JIT jest generalnie najpierw kompilowany do dość niskiego poziomu języka pośredniego, możesz i tak przeprowadzić wiele optymalizacji wysokiego poziomu w czasie kompilacji. Dużą zaletą jest to, że JIT może kontynuować rekompilację sekcji kodu w locie, ponieważ otrzymuje coraz więcej danych o tym, jak aplikacja jest używana. Może ustawić najczęstsze ścieżki kodu, aby umożliwić przewidywanie gałęzi tak często, jak to możliwe. Może ponownie rozmieścić osobne bloki kodu, które są często wywoływane razem, aby zachować je oba w pamięci podręcznej. Może poświęcić więcej wysiłku na optymalizację wewnętrznych pętli.

Wątpię, czy robi to .NET lub którykolwiek z JRE, ale badano go już na studiach, więc nie jest nierozsądne sądzić, że tego rodzaju rzeczy mogą wkrótce znaleźć się w prawdziwym świecie .


4

Aplikacje wymagające intensywnego dostępu do pamięci, np. manipulowanie obrazami jest zwykle lepiej napisane w środowisku niezarządzanym (C ++) niż zarządzanym (C #). Zoptymalizowane pętle wewnętrzne z arytmetyką wskaźników są znacznie łatwiejsze do kontrolowania w C ++. W języku C # konieczne może być użycie niebezpiecznego kodu, aby zbliżyć się do tej samej wydajności.


4

Testowałem vectorw C ++ i C # odpowiedniku - Listi prostych tablicach 2d.

Używam wersji Visual C # / C ++ 2010 Express. Oba projekty są prostymi aplikacjami konsolowymi, przetestowałem je w standardowym (bez ustawień niestandardowych) trybie wydania i debugowania. Listy C # działają szybciej na moim komputerze, inicjalizacja tablicy jest również szybsza w języku C #, operacje matematyczne są wolniejsze.

Używam Intel Core2Duo P8600@2.4GHz, C # - .NET 4.0.

Wiem, że implementacja wektorowa różni się od listy C #, ale chciałem tylko przetestować kolekcje, których użyłbym do przechowywania moich obiektów (i mogłem korzystać z modułu indeksującego).

Oczywiście musisz wyczyścić pamięć (powiedzmy za każdym razem new), ale chciałem, aby kod był prosty.

Test wektorowy C ++ :

static void TestVector()
{
    clock_t start,finish;
    start=clock();
    vector<vector<double>> myList=vector<vector<double>>();
    int i=0;
    for( i=0; i<500; i++)
    {
        myList.push_back(vector<double>());
        for(int j=0;j<50000;j++)
            myList[i].push_back(j+i);
    }
    finish=clock();
    cout<<(finish-start)<<endl;
    cout<<(double(finish - start)/CLOCKS_PER_SEC);
}

Test listy C #:

private static void TestVector()
{

    DateTime t1 = System.DateTime.Now;
    List<List<double>> myList = new List<List<double>>();
    int i = 0;
    for (i = 0; i < 500; i++)
    {
        myList.Add(new List<double>());
        for (int j = 0; j < 50000; j++)
            myList[i].Add(j *i);
    }
    DateTime t2 = System.DateTime.Now;
    Console.WriteLine(t2 - t1);
}

C ++ - tablica:

static void TestArray()
{
    cout << "Normal array test:" << endl;
    const int rows = 5000;
    const int columns = 9000;
    clock_t start, finish;

    start = clock();
    double** arr = new double*[rows];
    for (int i = 0; i < rows; i++)
        arr[i] = new double[columns];
    finish = clock();

    cout << (finish - start) << endl;

    start = clock();
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < columns; j++)
            arr[i][j] = i * j;
    finish = clock();

    cout << (finish - start) << endl;
}

C # - tablica:

private static void TestArray()
{
    const int rows = 5000;
    const int columns = 9000;
    DateTime t1 = System.DateTime.Now;
    double[][] arr = new double[rows][];
    for (int i = 0; i < rows; i++)
        arr[i] = new double[columns];
    DateTime t2 = System.DateTime.Now;

    Console.WriteLine(t2 - t1);

    t1 = System.DateTime.Now;
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < columns; j++)
            arr[i][j] = i * j;
    t2 = System.DateTime.Now;

    Console.WriteLine(t2 - t1);

}

Czas: (wydanie / debugowanie)

C ++

  • Inicjalizacja tablicy 600/606 ms,
  • Wypełnienie tablicy 200/270 ms,
  • 1 sekunda / 13 sekund inicjacja i wypełnienie wektora.

(Tak, 13 sekund, zawsze mam problemy z listami / wektorami w trybie debugowania).

DO#:

  • Inicjalizacja tablicy 20/20 ms,
  • Wypełnienie tablicy 403/440 ms,
  • 710/742 ms lista inicj. I wypeł.

1
Chciałbym zobaczyć akcesorium indeksu w std :: list. W każdym razie zajmuje to 37 sekund z listą, trybem zwolnienia. Wydanie bez debugowania: lista 3s, wektor 0,3 s. Prawdopodobnie problem dereferencji lub coś. Próbka: nopaste.pl/12fb
Wiory

2
Aby uzyskać bardziej precyzyjne pomiary, nie powinieneś używać System.DateTime.Now, ale raczej klasy Stopwatch .
Sam

4
Jednym z powodów, dla których otrzymujesz tak wolne czasy wypełnienia wektora w C ++, jest to, że używasz push_back. Zostało to wykazane w wielu postach jako wolniejsze niż przy użyciu metody at lub operatora []. Aby skorzystać z jednej z tych metod, musisz użyć metody zmiany rozmiaru lub rezerwacji. Dodatkowo powodem tak długiej inicjalizacji dla przypadku wektora c ++ jest wymuszenie operatora kopiowania lub przypisania (nie wiem, który w tym przypadku) do zainicjowania wektora c ++. Dla tablicy w c ++ istnieje algorytm, który wykorzystuje 2 nowe wywołania zamiast 5001 i jest również szybszy iteracyjny.
Zachary Kraus

5
Myślę, że nie zrobiłeś c ++ w odpowiedni sposób. Wystarczy rzut oka i znalazłem tak wiele problemów. Np. Wektor <wektor <podwój>> mojaLista = wektor <wektor <podwójnie>> ()
DAG,

2
Łał. Nie jestem pewien, jakie wnioski można wyciągnąć z porównania list w porównaniu z tablicami o zmiennym rozmiarze, ale jeśli zamierzasz używać takich wektorów, będziesz chciał dowiedzieć się o rezerwie (), mój przyjacielu, rezerwie ().
U007D,

3

Cóż, to zależy. Jeśli kod bajtowy zostanie przetłumaczony na kod maszynowy (a nie tylko JIT) (to znaczy, jeśli wykonasz program) i jeśli twój program używa wielu alokacji / dezalokacji, może być szybszy, ponieważ algorytm GC potrzebuje tylko jednego przejścia (teoretycznie) raz w całej pamięci, ale normalne wywołania malloc / realloc / free C / C ++ powodują obciążenie każdego połączenia (obciążenie wywołania, obciążenie struktury danych, brak pamięci podręcznej;)).

Jest to teoretycznie możliwe (także w przypadku innych języków GC).

Nie widzę tak naprawdę wady, że nie mogę używać metaprogramowania w C # dla większości aplikacji, ponieważ większość programistów i tak go nie używa.

Inną dużą zaletą jest to, że SQL, podobnie jak „rozszerzenie” LINQ , zapewnia kompilatorowi optymalizację wywołań do baz danych (innymi słowy, kompilator może skompilować całe LINQ do jednego pliku binarnego „blob”, w którym wywoływane funkcje są wstawiane lub zoptymalizowany do użytku, ale spekuluję tutaj).


1
Żaden właściwy programista C ++ nie napotka opisywanych problemów. Tylko zli programiści C, którzy postanowili spoliczkować klasy w swoich programach i nazywają to C ++, mają te problemy.
Jaśniejsze

1
na miłość boską, ma on 8 lat, OMFGz
Quonux

nie
wahaj

2

Sądzę, że istnieją aplikacje napisane w C # działające szybko, a także więcej aplikacji napisanych w C ++ działających szybko (no C ++ jest tylko starszy ... i weź też UNIX ...)
- pytanie w istocie jest - co to jest, użytkownicy a programiści narzekają na ...
Cóż, IMHO, w przypadku C # mamy bardzo wygodny interfejs użytkownika, bardzo ładną hierarchię bibliotek i cały system interfejsu CLI. W przypadku C ++ mamy szablony, ATL, COM, MFC i całą sekwencję napisanego i działającego kodu alreadyc, takiego jak OpenGL, DirectX itd. ... Programiści narzekają na nieokreślone wzrosty wywołań GC w przypadku C # (oznacza to, że program działa szybko i w sekundę - huk! utknął).
Pisanie kodu w C # jest bardzo proste i szybkie (nie zapominając o tym, że zwiększają również ryzyko błędów. W przypadku C ++ programiści narzekają na wycieki pamięci, - oznaczają kruszenia, wywołania między bibliotekami DLL, a także „DLL hell” - problem z obsługa i zastępowanie bibliotek nowszymi ...
Myślę, że więcej umiejętności będziesz mieć w języku programowania, tym więcej jakości (i szybkości) będzie charakteryzowało twoje oprogramowanie.


2

Powiedziałbym to w ten sposób: programiści, którzy piszą szybszy kod, są tymi, którzy są bardziej poinformowani o tym, co sprawia, że ​​obecne maszyny działają szybko, a przy okazji, oni też używają odpowiedniego narzędzia, które pozwala na precyzyjne określenie poziomu niskiego i deterministyczne techniki optymalizacji. Z tych powodów ci ludzie używają C / C ++ zamiast C #. Poszedłbym tak daleko, stwierdzając to jako fakt.


Minecraft zakodował wycięcie, aby było dość szybkie, biorąc pod uwagę ilość danych, którymi manipuluje. Ponadto kodował go głównie samodzielnie w stosunkowo krótkim czasie, co w C ++ byłoby praktycznie niemożliwe. Zgadzam się jednak z technikami optymalizacji - jeśli masz dodatkowy 10-krotny czas na opracowanie, więc Twój kod działa dwa razy szybciej, prawdopodobnie warto.
Bill K

2

Jeśli się nie mylę, szablony C # są określane w czasie wykonywania. To musi być wolniejsze niż szablony czasu kompilacji C ++.

A jeśli weźmiesz pod uwagę wszystkie inne optymalizacje czasu kompilacji wspomniane przez tak wielu innych, a także brak bezpieczeństwa, który w rzeczywistości oznacza większą prędkość ...

Powiedziałbym, że C ++ jest oczywistym wyborem pod względem surowej prędkości i minimalnego zużycia pamięci. Ale to również przekłada się na więcej czasu na opracowanie kodu i upewnienie się, że nie przeciekasz pamięci ani nie powodujesz wyjątków wskaźnika zerowego.

Werdykt:

  • C #: Szybszy rozwój, wolniejszy bieg

  • C ++: Powolny rozwój, szybszy bieg.


1

To naprawdę zależy od tego, co próbujesz osiągnąć w kodzie. Słyszałem, że to tylko miejska legenda, że ​​istnieje różnica w wydajności między VB.NET, C # i zarządzanym C ++. Jednak odkryłem, przynajmniej w porównaniach ciągów, że zarządzanie C ++ bije spodnie od C #, co z kolei bije spodnie od VB.NET.

W żadnym wypadku nie przeprowadziłem wyczerpujących porównań złożoności algorytmicznej między językami. Używam również ustawień domyślnych w każdym z języków. W VB.NET używam ustawień, aby wymagać deklaracji zmiennych itp. Oto kod, którego używam do zarządzanego C ++: (Jak widać, ten kod jest dość prosty). Używam tego samego w innych językach w Visual Studio 2013 z .NET 4.6.2.

#include "stdafx.h"

using namespace System;
using namespace System::Diagnostics;

bool EqualMe(String^ first, String^ second)
{
    return first->Equals(second);
}
int main(array<String ^> ^args)
{
    Stopwatch^ sw = gcnew Stopwatch();
    sw->Start();
    for (int i = 0; i < 100000; i++)
    {
        EqualMe(L"one", L"two");
    }
    sw->Stop();
    Console::WriteLine(sw->ElapsedTicks);
    return 0;
}

1

Istnieją pewne główne różnice między C # i C ++ pod względem wydajności:

  • C # jest oparty na GC / stercie. Alokacja i sam GC są narzutem jako brak lokalizacji dostępu do pamięci
  • Optymalizatory C ++ stały się bardzo dobre na przestrzeni lat. Kompilatory JIT nie mogą osiągnąć tego samego poziomu, ponieważ mają ograniczony czas kompilacji i nie widzą zasięgu globalnego

Oprócz tego ważną rolę odgrywają także kompetencje programisty. Widziałem zły kod C ++, w którym klasy były przekazywane przez wartość jako argument w każdym miejscu. Możesz faktycznie pogorszyć wydajność w C ++, jeśli nie wiesz, co robisz.


0

> W końcu odpowiedzi muszą gdzieś być, prawda? :)

Umm nie.

Jak zauważono w kilku odpowiedziach, pytanie jest niedokładnie określone w sposób zachęcający do odpowiedzi w odpowiedzi, a nie do odpowiedzi. Aby wziąć tylko jeden sposób:

A potem które programy? Która maszyna? Który system operacyjny? Który zestaw danych


W pełni się zgadzam. Zastanawiam się, dlaczego ludzie oczekują precyzyjnej odpowiedzi (63,5%), kiedy zadają ogólne pytanie. Nie wydaje mi się, żeby na takie pytanie nie było ogólnej odpowiedzi.
zadzwoń do mnie Steve

@callmesteve: Wiem, co masz na myśli, ale twoje ostatnie zdanie powinno brzmieć jak gwoździe na tablicy dla każdego programisty.
Wouter van Nifterick,

1
To nie wydaje się odpowiadać na pytanie i czyta więcej jako komentarz lub rant.
Tas

-13

Zainspirowany tym, zrobiłem szybki test z 60 procentami powszechnych instrukcji potrzebnych w większości programów.

Oto kod C #:

for (int i=0; i<1000; i++)
{
    StreamReader str = new StreamReader("file.csv");
    StreamWriter stw = new StreamWriter("examp.csv");
    string strL = "";
    while((strL = str.ReadLine()) != null)
    {
        ArrayList al = new ArrayList();
        string[] strline = strL.Split(',');
        al.AddRange(strline);
        foreach(string str1 in strline)
        {
            stw.Write(str1 + ",");
        }
        stw.Write("\n");
    }
    str.Close();
    stw.Close();
}

Tablica łańcuchów i lista arraylistów są celowo stosowane w celu uwzględnienia tych instrukcji.

Oto kod c ++:

for (int i = 0; i<1000; i++)
{
    std::fstream file("file.csv", ios::in);
    if (!file.is_open())
    {
        std::cout << "File not found!\n";
        return 1;
    }

    ofstream myfile;
    myfile.open ("example.txt");
    std::string csvLine;

    while (std::getline(file, csvLine))
    {
        std::istringstream csvStream(csvLine);
        std::vector csvColumn;
        std::string csvElement;

        while( std::getline(csvStream, csvElement, ‘,’) )
        {
            csvColumn.push_back(csvElement);
        }

        for (std::vector::iterator j = csvColumn.begin(); j != csvColumn.end(); ++j)
        {
            myfile << *j << ", ";
        }

        csvColumn.clear();
        csvElement.clear();
        csvLine.clear();
        myfile << "\n";
    }
    myfile.close();
    file.close();
}

Rozmiar użytego pliku wejściowego wynosił 40 KB.

A oto wynik -

  • Kod C ++ uruchomiono w 9 sekund.
  • Kod C #: 4 sekundy !!!

Och, ale to było na Linuksie ... Z C # działającym na Mono ... I C ++ z g ++.

OK, oto co mam w systemie Windows - Visual Studio 2003 :

  • Kod C # uruchomiono w 9 sekund.
  • Kod C ++ - okropne 370 sekund !!!

7
Używasz tam różnych struktur danych i kodu biblioteki, chociaż „370 sekund” wskazuje na coś okropnego - przypadkiem nie uruchamiasz go w debuggerze? Podejrzewam, że wydajność używanej biblioteki CSV jest bardziej interesująca niż wydajność używanego języka. Chciałbym zakwestionować użycie wektora w tym kontekście i jakie zastosowałeś optymalizacje. Ponadto powszechnie wiadomo, że iostreams (w szczególności „myfile << * j <<”, „;”) jest znacznie wolniejszy niż inne metody zapisu do pliku, przynajmniej w niektórych typowych implementacjach.
Arafangion

6
Wreszcie, wykonujesz więcej pracy w wersji C ++. (Dlaczego czyścisz csvColumn, csvElement i csvLines?)
Arafangion

2
Każda iteracja pętli while spowoduje zniszczenie i zrekonstruowanie std :: istream oraz std :: vector i std :: string. Podczas gdy ciało podczas każdej iteracji wychodzi poza zakres, wszystkie zmienne wewnątrz zakresu podczas niszczą się i konstruują przy każdej iteracji.
doug65536

1
z wyglądu czytanego kodu c ++, który próbujesz skopiować z jednego pliku do drugiego. Zamiast korzystać ze złożonych interakcji między strumieniami plików, ciągami, wektorami i strumieniami ciągów, mógłbyś właśnie skopiować strumień pliku wejściowego do strumienia pliku wyjściowego. Zaoszczędziłoby to dużo czasu i pamięci.
Zachary Kraus

2
aby wykonać testy prędkości, przetestować rzeczy w pamięci, nie dostań się do dysku IO, chyba że testujesz na najnowszych dyskach SSD i jest on dedykowany twojej aplikacji wydajności. Ponieważ komputery stale zapisują na dysku, nawet jeśli nie dotkniesz klawiatury.
user3800527 12.04.16
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.