Słuchając podcastu StackOverflow, pojawia się myśl, że „prawdziwi programiści” piszą w C, i że C jest o wiele szybszy, ponieważ jest „blisko maszyny”. Pozostawiając poprzednie stwierdzenie dla innego postu, co jest specjalnego w C, które pozwala mu być szybsze niż inne języki? Lub inaczej: co powstrzyma inne języki przed kompilacją do wersji binarnej, która działa tak samo szybko jak C?
c
performance
Mike C.
źródło
źródło
Odpowiedzi:
Nie ma nic szczególnego w C. Jest to jeden z powodów, dla których jest szybki.
Nowsze języki obsługujące zbieranie śmieci , dynamiczne pisanie i inne funkcje ułatwiające programistom pisanie programów.
Problem polega na tym, że istnieje dodatkowy narzut przetwarzania, który obniży wydajność aplikacji. C nie ma tego, co oznacza, że nie ma narzutu, ale to oznacza, że programista musi być w stanie przydzielić pamięć i zwolnić je, aby zapobiec wyciekom pamięci , i musi radzić sobie ze statycznym typowaniem zmiennych.
To powiedziawszy, wiele języków i platform, takich jak Java (z jej wirtualną maszyną Java ) i .NET (z jego wspólnym środowiskiem uruchomieniowym) poprawiły wydajność na przestrzeni lat dzięki takim rozwiązaniom, jak kompilacja just-in-time, która wytwarza natywny kod maszynowy z bytecode w celu osiągnięcia wyższej wydajności.
źródło
Projektanci C dokonali kompromisu. To znaczy, podjęli decyzję o podniesieniu prędkości ponad bezpieczeństwo. C nie chce
Podczas indeksowania do tablicy w Javie wymaga ona wywołania metody na maszynie wirtualnej, sprawdzania powiązań i innych sprawdzeń poprawności. Jest to ważne i absolutnie w porządku , ponieważ zwiększa bezpieczeństwo tam, gdzie jest to konieczne. Ale w C nawet dość trywialne rzeczy nie są bezpieczne. Na przykład C nie wymaga memcpy do sprawdzenia, czy regiony do skopiowania nakładają się. To nie zaprojektowany jako język do programowania duży aplikacji biznesowych.
Ale te decyzje projektowe są nie błędy w języku C . Są one zaprojektowane, ponieważ pozwalają kompilatorom i autorom bibliotek uzyskać wszelką wydajność z komputera. Oto duch C, w jaki sposób wyjaśnia to C Rationale :
źródło
Jeśli spędzasz miesiąc, aby zbudować coś w C, które działa w 0,05 sekundy, a ja spędzam dzień pisząc to samo w Javie i działa w 0,10 sekundy, to czy C jest naprawdę szybsze?
Ale aby odpowiedzieć na twoje pytanie, dobrze napisany kod C będzie generalnie działał szybciej niż dobrze napisany kod w innych językach, ponieważ część pisania „dobrze” kodu C obejmuje robienie ręcznych optymalizacji na poziomie zbliżonym do maszynowego.
Chociaż kompilatory są naprawdę bardzo sprytne, nie są jeszcze w stanie twórczo wymyślić kodu, który konkuruje z algorytmami masowanymi ręcznie (zakładając, że „ręce” należą do dobrego programisty w języku C).
Edytować:
Wiele komentarzy jest w stylu „Piszę w C i nie myślę o optymalizacjach”.
Ale biorąc konkretny przykład z tego postu :
W Delphi mógłbym napisać:
i w CI napisz to:
Ale ile jest optymalizacji w wersji C? Podejmujemy wiele decyzji dotyczących implementacji, o których nie myślę w wersji Delphi. Jak wdrażany jest ciąg? W Delphi nie widzę tego. W C zdecydowałem, że będzie to wskaźnik do tablicy liczb całkowitych ASCII, którą nazywamy znakami. W C testujemy istnienie postaci pojedynczo. W Delphi używam Poz.
A to tylko mały przykład. W dużym programie programista C musi podejmować tego rodzaju decyzje niskiego poziomu przy każdej linii kodu. Daje to ręcznie wykonany, zoptymalizowany ręcznie plik wykonywalny.
źródło
Nie widziałem go już, więc powiem to: C wydaje się być szybciej, ponieważ prawie wszystko jest napisany w C .
Java jest zbudowana na C, Python na C (lub Java, .NET itp.), Perl jest itp. System operacyjny jest napisany w C, maszyny wirtualne są napisane w C, kompilatory są napisane w C, tłumacze są napisani w C. Niektóre rzeczy są nadal napisane w języku asemblera, który zwykle jest jeszcze szybszy. Coraz więcej rzeczy jest pisanych w czymś innym, co samo jest napisane w C.
Każda instrukcja napisana w innych językach (nie asemblerowych) jest zwykle implementowana pod spodem jako kilka instrukcji w C, które są kompilowane do natywnego kodu maszynowego. Ponieważ te inne języki zwykle istnieją w celu uzyskania wyższego poziomu abstrakcji niż C, te dodatkowe stwierdzenia wymagane w C zwykle koncentrują się na zwiększeniu bezpieczeństwa, złożoności i obsłudze błędów. Często są to dobre rzeczy, ale mają swój koszt , a ich nazwy to szybkość i rozmiar .
Osobiście pisałem w dosłownie kilkudziesięciu językach obejmujących większość dostępnego spektrum i osobiście szukałem magii, o której wspominacie:
Po kilku latach badań moja odpowiedź brzmi: Python (na C). Możesz rzucić okiem. Nawiasem mówiąc, możesz również przejść do Asemblera z Pythona (z niewielką pomocą specjalnej biblioteki).
Z drugiej strony zły kod można napisać w dowolnym języku . Dlatego kod C (lub asembler) nie jest automatycznie szybszy. Podobnie, niektóre sztuczki optymalizacyjne mogą zbliżyć części kodu języka wyższego poziomu do poziomu wydajności surowego C. Jednak w przypadku większości aplikacji program spędza większość czasu na ludziach lub sprzęcie, więc różnica naprawdę nie ma znaczenia.
Cieszyć się.
źródło
Jest tam wiele pytań - głównie na te, na które nie mam kwalifikacji. Ale w tym ostatnim:
Jednym słowem, abstrakcja.
C to tylko jeden lub dwa poziomy abstrakcji od języka maszynowego. Języki Java i .Net znajdują się na minimum 3 poziomach abstrakcji od asemblera. Nie jestem pewien co do Pythona i Ruby.
Zazwyczaj im więcej zabawek dla programistów (złożone typy danych itp.), Tym bardziej jesteś w języku maszynowym i tym więcej trzeba wykonać tłumaczenia.
Jestem tu i tam, ale to jest podstawowa istota.
Aktualizacja ------- Ten post zawiera kilka dobrych komentarzy z dodatkowymi szczegółami.
źródło
C nie jest tak szybki, że model kosztów tego C jest przejrzysty . Jeśli program C jest wolny, działa w oczywisty sposób: wykonując wiele instrukcji. W porównaniu z kosztem operacji w C operacje wysokiego poziomu na obiektach (szczególnie odbicie) lub łańcuchach mogą mieć koszty, które nie są oczywiste.
Dwa języki, które generalnie kompilują się do plików binarnych, które są równie szybkie jak C, to Standard ML (przy użyciu kompilatora MLton ) i Objective Caml . Jeśli przejrzysz grę z testami porównawczymi, zauważysz, że w przypadku niektórych testów porównawczych, takich jak drzewa binarne, wersja OCaml jest szybsza niż C. (Nie znalazłem żadnych wpisów MLton). Ale nie traktuj strzelaniny zbyt poważnie; jak mówi gra, wyniki często odzwierciedlają wysiłek włożony w dostosowanie kodu.
źródło
C nie zawsze jest szybszy.
C jest wolniejszy niż, na przykład Modern Fortran.
W niektórych przypadkach C jest często wolniejszy niż Java. (Zwłaszcza po uruchomieniu kompilatora JIT w kodzie)
C pozwala na aliasing wskaźnika, co oznacza, że niektóre dobre optymalizacje nie są możliwe. Zwłaszcza gdy masz wiele jednostek wykonawczych, powoduje to przeciąganie pobierania danych. Ow.
Założenie, że arytmetyka wskaźników działa naprawdę powoduje powolną nadmierną wydajność w niektórych rodzinach procesorów (szczególnie PIC!). Kiedyś ssała dużą na segmentowanym x86.
Zasadniczo, gdy otrzymujesz jednostkę wektorową lub kompilator równoległy, C śmierdzi, a nowoczesny Fortran działa szybciej.
Sztuczki programisty C, takie jak thunking (modyfikowanie pliku wykonywalnego w locie), powodują zatrzymanie pobierania procesora.
Masz dryf?
A nasz dobry przyjaciel, x86, wykonuje zestaw instrukcji, który obecnie ma niewielki związek z rzeczywistą architekturą procesora. Rejestry cienia, optymalizatory przechowujące ładunki, wszystko w CPU. Zatem C jest blisko wirtualnego metalu. Prawdziwy metal, Intel nie pozwala ci zobaczyć. (Historycznie procesory VLIW były trochę bolesne, więc może nie jest tak źle.)
Jeśli programujesz w C na wysokowydajnym procesorze DSP (może TI DSP?), Kompilator musi wykonać pewne trudne czynności, aby rozwinąć C w wielu równoległych jednostkach wykonawczych. W takim przypadku C nie jest bliski metalowi, ale jest blisko kompilatora, który dokona optymalizacji całego programu. Dziwne.
Na koniec niektóre procesory (www.ajile.com) obsługują sprzętowe kody bajtowe Java. C użyłby PITA na tym CPU.
źródło
Nic. Współczesne języki, takie jak Java lub .NET, są bardziej zorientowane na wydajność programisty niż na wydajność. Sprzęt jest teraz tani. Również kompilacja do pośredniej reprezentacji daje wiele bonusów, takich jak bezpieczeństwo, przenośność itp. .NET CLR może korzystać z innego sprzętu - na przykład nie trzeba ręcznie optymalizować / rekompilować programu, aby użyć zestawu instrukcji SSE.
źródło
Głównymi czynnikami są to, że jest to język o typie statycznym i skompilowany do kodu maszynowego. Ponadto, ponieważ jest to język niskiego poziomu, na ogół nie robi nic, o czym mu nie powiesz.
Są to inne czynniki, które przychodzą na myśl.
Jednak większość języków o typie statycznym można skompilować równie szybko lub szybciej niż C, zwłaszcza jeśli mogą przyjąć założenia, że C nie może z powodu aliasingu wskaźnika itp.
źródło
Chyba zapomniałeś, że język asemblera to także język :)
Ale poważnie, programy C są szybsze tylko wtedy, gdy programista wie, co robi. Możesz łatwo napisać program C, który działa wolniej niż programy napisane w innych językach, które wykonują to samo zadanie.
Powodem, dla którego C jest szybszy, jest to, że jest zaprojektowany w ten sposób. Pozwala ci robić wiele rzeczy „niższego poziomu”, które pomagają kompilatorowi zoptymalizować kod. Lub, powiedzmy, programista jest odpowiedzialny za optymalizację kodu. Ale często jest to dość trudne i podatne na błędy.
Inne języki, jak już wspomniano, koncentrują się bardziej na produktywności programisty. Powszechnie uważa się, że czas programisty jest znacznie droższy niż czas maszynowy (nawet w dawnych czasach). Dlatego rozsądne jest zminimalizowanie czasu, jaki programiści spędzają na pisaniu i debugowaniu programów zamiast czasu działania programów. Aby to zrobić, poświęcisz nieco to, co możesz zrobić, aby przyspieszyć program, ponieważ wiele rzeczy jest zautomatyzowanych.
źródło
W przeważającej części każda instrukcja C odpowiada bardzo niewielu instrukcjom asemblera. Zasadniczo piszesz kod maszynowy wyższego poziomu, więc masz kontrolę nad prawie wszystkim, co robi procesor. Wiele innych skompilowanych języków, takich jak C ++, ma wiele prostych instrukcji, które mogą przekształcić się w znacznie więcej kodu, niż myślisz (funkcje wirtualne, konstruktory kopiowania itp.). A języki interpretowane, takie jak Java lub Ruby, mają kolejną warstwę instrukcje, których nigdy nie widzisz - maszyna wirtualna lub tłumacz.
źródło
Wiem, że wiele osób powiedziało to na długi czas, ale:
źródło
C ++ jest średnio szybszy (tak jak początkowo był w dużej mierze nadzbiorem C, choć istnieją pewne różnice). Jednak w przypadku określonych testów porównawczych często istnieje inny język, który jest szybszy.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/
fannjuch-redux
był najszybszy w Scalin-body
ifasta
byli szybsi w Adzie.spectral-norm
był najszybszy w Fortranie.reverse-complement
,mandelbrot
Ipidigits
były najszybsze w ATS.regex-dna
był najszybszy w JavaScript.chameneou-redux
był najszybszy to Java 7.thread-ring
był najszybszy w Haskell.Pozostałe testy porównawcze były najszybsze w C lub C ++.
źródło
extern "C"
nie ma nic wspólnego z C ++ będącym nadzbiorem C.system("bash script.sh");
działa dla każdego skryptu bash, a zatem C jest nadzbiorem bash.extern "C"
zapewnia połączenie C w C ++ z powodu zmiany nazwy. Podczas gdy wywoływanie X jako nadzbiór Y oznacza, że wszystko, co można zrobić w Y, można również zrobić w X, co nie jest prawdą w C ++. Istnieje wiele konstrukcji językowych, które są poprawne w C, ale nie w C ++.bash
do korzystania z dostępnego programu wiersza poleceń. Gdyby to zrobił i zawierał wersję / specyfikację bash, która powinna być obsługiwana, uznałbym to za nadzbiór.struct foo { int: this; }; typedef float foo;
Wiele z tych odpowiedzi podaje uzasadnione powody, dla których C jest lub nie jest szybsze (ogólnie lub w określonych scenariuszach). Jest niezaprzeczalne, że:
Mimo to, zauważyłem coś jeszcze, co, jak sądzę, wpływa bardziej na porównawczą wydajność C w porównaniu do wielu innych języków bardziej niż jakikolwiek inny czynnik. To znaczy:
Inne języki często ułatwiają pisanie kodu, który działa wolniej. Często jest to wspierane przez filozofie projektowania języka. Następstwo: programiści C częściej piszą kod, który nie wykonuje niepotrzebnych operacji.
Jako przykład rozważ prosty program Windows, w którym tworzone jest pojedyncze okno główne. Wersja AC wypełniłaby
WNDCLASS[EX]
strukturę, która byłaby przekazanaRegisterClass[Ex]
, a następnie wywołałaCreateWindow[Ex]
i wprowadziła pętlę komunikatów. Wysoce uproszczony i skrócony kod wygląda następująco:Odpowiednikiem programu w języku C # może być tylko jeden wiersz kodu:
Ten jeden wiersz kodu zapewnia wszystkie funkcje, które zrobiło prawie 20 wierszy kodu C, i dodaje pewne rzeczy, które pominęliśmy, takie jak sprawdzanie błędów. Bogatsza, pełniejsza biblioteka (w porównaniu do tych używanych w typowym projekcie C) wykonała dla nas dużo pracy, uwalniając nasz czas na pisanie wielu fragmentów kodu, które są dla nas krótkie, ale wymagają wielu kroków za kulisami.
Ale bogata biblioteka umożliwiająca łatwe i szybkie rozszerzanie kodu nie jest moim celem. Moja uwaga jest bardziej widoczna, gdy zaczynasz badać, co faktycznie dzieje się, gdy nasz mały liniowiec faktycznie wykonuje. Czasami dla zabawy włącz dostęp do źródła .NET w Visual Studio 2008 lub nowszym i przejdź do prostej linijki powyżej. Jednym z zabawnych małych klejnotów, które można napotkać, jest ten komentarz na forum dla
Control.CreateParams
:Dziesięć razy . Informacje w przybliżeniu równoważne sumie tego, co jest przechowywane w
WNDCLASSEX
strukturze i tego, co jest przekazywane,CreateWindowEx
jest pobierane zControl
klasy dziesięć razy, zanim zostanie zapisane wWNDCLASSEX
strukturze i przekazane doRegisterClassEx
iCreateWindowEx
.Podsumowując, liczba instrukcji wykonanych w celu wykonania tego bardzo podstawowego zadania wynosi 2–3 rzędy wielkości więcej w C # niż w C. Część tego wynika z użycia bogatej w funkcje biblioteki, która jest koniecznie uogólniona, w porównaniu z nasz prosty kod C, który robi dokładnie to, czego potrzebujemy i nic więcej. Ale częściowo wynika to z faktu, że modularna, zorientowana obiektowo natura frameworku .NET pozwala na wiele powtórzeń wykonywania, których często można uniknąć dzięki podejściu proceduralnemu.
Nie próbuję wybrać C # lub .NET. Nie mówię też, że modularyzacja, uogólnienie, funkcje biblioteki / języka, OOP itp. Są złymi rzeczami . Zwykle zajmowałem się głównie programowaniem w C, później w C ++, a ostatnio w C #. Podobnie przed C korzystałem głównie z montażu. I z każdym krokiem „wyżej” idzie mój język, piszę lepsze, łatwiejsze w utrzymaniu, bardziej niezawodne programy w krótszym czasie. Z reguły jednak wykonują się nieco wolniej.
źródło
Nie sądzę, żeby ktokolwiek wspominał o tym, że włożono dużo więcej wysiłku w kompilatory C niż jakikolwiek inny kompilator, być może z wyjątkiem Javy.
C jest niezwykle zoptymalizowany z wielu już podanych powodów - więcej niż prawie jakikolwiek inny język. Więc jeśli włożymy tyle samo wysiłku w kompilatory w innych językach, C prawdopodobnie nadal będzie na topie.
Myślę, że istnieje co najmniej jeden język kandydujący, który z wysiłkiem można zoptymalizować lepiej niż C, a zatem możemy zobaczyć implementacje, które produkują szybsze pliki binarne. Myślę o cyfrowym marsie D, ponieważ twórca zadbał o zbudowanie języka, który mógłby być lepiej zoptymalizowany niż C. Mogą istnieć inne języki, które mają taką możliwość. Jednak nie mogę sobie wyobrazić, że każdy język będzie miał kompilatory więcej niż zaledwie kilka procent szybsze niż najlepsze kompilatory C. Chciałbym się mylić.
Myślę, że prawdziwy „nisko wiszący owoc” będzie w językach, które zostały zaprojektowane tak, aby były ŁATWE dla ludzi w celu optymalizacji. Utalentowany programista może sprawić, że każdy język będzie działał szybciej - ale czasami musisz robić absurdalne rzeczy lub używać nienaturalnych konstrukcji, aby tak się stało. Chociaż zawsze będzie to wymagało wysiłku, dobry język powinien generować stosunkowo szybki kod bez obsesji na punkcie tego, jak napisany jest program.
Ważne jest również (przynajmniej dla mnie), aby najgorszy kod był zwykle szybki. W sieci istnieje wiele „dowodów” na to, że Java jest tak szybka lub szybsza niż C, ale opiera się ona na przykładach kompletacji. Nie jestem wielkim fanem C, ale wiem, że WSZYSTKO, co piszę w C, będzie działało dobrze. W Javie „prawdopodobnie” będzie działać w granicach 15% prędkości, zwykle w granicach 25%, ale w niektórych przypadkach może być znacznie gorzej. Wszelkie przypadki, gdy jest tak samo szybkie lub w granicach kilku procent, są zwykle spowodowane spędzaniem większości czasu w kodzie biblioteki, który i tak jest mocno zoptymalizowany C.
źródło
To jest właściwie utrwalone kłamstwo. Chociaż prawdą jest, że programy C są często szybsze, nie zawsze tak jest, szczególnie jeśli programista C nie jest w tym zbyt dobry.
Jedną z wielkich rażących dziur, o których ludzie często zapominają, jest to, że program musi blokować jakieś operacje wejścia / wyjścia, na przykład dane wprowadzone przez użytkownika w dowolnym programie GUI. W takich przypadkach nie ma znaczenia, jakiego języka używasz, ponieważ jesteś ograniczony szybkością, z jaką dane mogą przychodzić, a nie szybkością przetwarzania. W tym przypadku nie ma większego znaczenia, jeśli używasz C, Java, C #, a nawet Perla; po prostu nie możesz iść szybciej niż dane mogą wejść.
Inną ważną rzeczą jest to, że korzystanie z odśmiecania pamięci i niestosowanie odpowiednich wskaźników pozwala maszynie wirtualnej na wykonanie szeregu optymalizacji niedostępnych w innych językach. Na przykład JVM jest w stanie przesuwać obiekty na stercie, aby ją zdefragmentować. To sprawia, że przyszłe alokacje są znacznie szybsze, ponieważ można po prostu użyć następnego indeksu zamiast szukać go w tabeli. Współczesne maszyny JVM również nie muszą faktycznie zwalniać pamięci; zamiast tego po prostu przesuwają żywe obiekty, gdy GC, a pamięć z martwych obiektów jest odzyskiwana zasadniczo za darmo.
Przywołuje to również interesującą kwestię dotyczącą C, a tym bardziej w C ++. Jest coś w rodzaju filozofii projektowania: „Jeśli nie potrzebujesz, nie płacisz za to”. Problem polega na tym, że jeśli tego chcesz, w końcu płacisz za to przez nos. Na przykład implementacja vtable w Javie jest znacznie lepsza niż implementacje C ++, więc wywołania funkcji wirtualnych są znacznie szybsze. Z drugiej strony, nie masz wyboru, jak korzystać z funkcji wirtualnych w Javie i nadal coś kosztują, ale w programach, które używają wielu funkcji wirtualnych, obniżony koszt sumuje się.
źródło
Nie chodzi tylko o język, ale o narzędzia i biblioteki. Dostępne biblioteki i kompilatory dla C są znacznie starsze niż dla nowszych języków. Możesz pomyśleć, że to spowolni je, ale au contraire.
Te biblioteki zostały napisane w czasie, gdy moc obliczeniowa i pamięć były na wagę złota. Musiały być napisane bardzo skutecznie, aby w ogóle pracować. Deweloperzy kompilatorów C również długo pracowali przy różnego rodzaju sprytnych optymalizacjach dla różnych procesorów. Dojrzałość C i szerokie zastosowanie przysparza znaczącej przewagi nad innymi językami w tym samym wieku. Daje też C przewagę prędkości w porównaniu z nowszymi narzędziami, które nie kładą tak dużego nacisku na wydajność jak C musiał.
źródło
Brak abstrakcji sprawia, że C jest szybszy. Jeśli napiszesz instrukcję wyjściową, wiesz dokładnie, co się dzieje. Jeśli napiszesz instrukcję wyjściową w java, kompiluje się ona do pliku klasy, który następnie uruchamia się na maszynie wirtualnej, wprowadzając warstwę abstrakcji. Brak funkcji obiektowych jako części języka zwiększa także jego szybkość, aby generować mniej kodu. Jeśli używasz C jako języka zorientowanego obiektowo, wtedy wykonujesz całe kodowanie dla takich rzeczy, jak klasy, niewiedza itp. Oznacza to, że zamiast tego utwórz coś wystarczająco uogólnionego dla każdego z ilością kodu i wydajnością wymagającą tylko pisania czego potrzebujesz, aby wykonać zadanie.
źródło
Niesamowite jest to, że stary „C / C ++ musi być szybszy niż Java, ponieważ Java jest interpretowana” mit wciąż żyje i ma się dobrze. Istnieją artykuły sprzed kilku lat , a także nowsze , które wyjaśniają pojęciami lub pomiarami, dlaczego po prostu nie zawsze tak jest .
Obecne implementacje maszyn wirtualnych (a przy okazji nie tylko JVM) mogą korzystać z informacji zebranych podczas wykonywania programu, aby dynamicznie dostroić kod podczas jego działania, używając różnych technik:
oraz szereg innych korekt opartych na wiedzy o tym, co faktycznie robi kod i na faktycznych cechach środowiska, w którym działa.
źródło
Najszybszym działającym kodem byłby starannie wykonany ręcznie kod maszynowy. Asembler będzie prawie tak samo dobry. Oba są na bardzo niskim poziomie i zajmuje dużo pisania kodu. C jest nieco powyżej asemblera. Nadal masz możliwość kontrolowania rzeczy na bardzo niskim poziomie w rzeczywistej maszynie, ale jest wystarczająca abstrakcja, dzięki czemu pisanie jej jest szybsze i łatwiejsze niż w asemblerze. Inne języki, takie jak C # i JAVA, są jeszcze bardziej abstrakcyjne. Podczas gdy asembler i kod maszynowy nazywane są językami niskiego poziomu, C # i JAVA (i wiele innych) nazywane są językami wysokiego poziomu. C jest czasem nazywany językiem średniego poziomu.
źródło
Nie wierz komuś na słowo, spójrz na demontaż zarówno C, jak i wybranego języka w dowolnej części kodu o kluczowym znaczeniu dla wydajności. Myślę, że możesz po prostu spojrzeć w oknie demontażu w czasie wykonywania w Visual Studio, aby zobaczyć zdemontowany .Net. Powinno być to możliwe, jeśli jest to trudne dla Javy przy użyciu windbg, ale jeśli zrobisz to z .Net, wiele problemów będzie takich samych.
Nie lubię pisać w C, jeśli nie muszę, ale myślę, że wiele twierdzeń zawartych w tych odpowiedziach, które mówią o szybkości języków innych niż C, można odrzucić, po prostu rozkładając tę samą procedurę w C i w wybranym przez Ciebie języku wyższego poziomu, szczególnie jeśli w grę wchodzi dużo danych, co jest powszechne w aplikacjach o kluczowym znaczeniu dla wydajności. Fortran może być wyjątkiem w dziedzinie wiedzy specjalistycznej, nie wiem. Czy to wyższy poziom niż C?
Za pierwszym razem porównałem kod JITed z natywnym kodem, rozwiązałem wszystkie pytania, czy kod .Net może działać porównywalnie z kodem C. Dodatkowy poziom abstrakcji i wszystkie kontrole bezpieczeństwa wiążą się ze znacznymi kosztami. Java miałaby prawdopodobnie takie same koszty, ale nie wierz mi na słowo, wypróbuj coś, w którym wydajność ma kluczowe znaczenie. (Czy ktoś wie wystarczająco dużo o JITed Java, aby znaleźć skompilowaną procedurę w pamięci? Z pewnością powinno to być możliwe)
źródło
1) Jak powiedzieli inni, C robi dla ciebie mniej. Bez inicjowania zmiennych, bez sprawdzania granic tablic, bez zarządzania pamięcią itp. Te funkcje w innych językach kosztują pamięć i cykle procesora, których C nie wydaje.
2) Odpowiedzi stwierdzające, że C jest mniej abstrakcyjne, a zatem szybsze są tylko w połowie poprawne. Technicznie rzecz biorąc, jeśli masz „wystarczająco zaawansowany kompilator” dla języka X, wówczas język X może zbliżyć się do prędkości C. bezpośrednio do języka asemblera, który nawet naiwny kompilator może wykonać przyzwoitą robotę. Do czegoś takiego jak Python potrzebujesz bardzo zaawansowanego kompilatora do przewidywania prawdopodobnych typów obiektów i generowania kodu maszynowego w locie - semantyka C jest na tyle prosta, że prosty kompilator może sobie poradzić.
źródło
W dobrych czasach istniały tylko dwa rodzaje języków: kompilowany i interpretowany.
Języki skompilowane wykorzystywały „kompilator” do odczytywania składni języka i konwertowania go na identyczny kod języka asemblerowego, który mógłby nie tylko bezpośrednio na CPU. Języki interpretowane wykorzystywały kilka różnych schematów, ale zasadniczo składnia języka została przekonwertowana na formę pośrednią, a następnie uruchomiona w „tłumaczu”, środowisku do wykonywania kodu.
Tak więc w pewnym sensie istniała inna „warstwa” - interpreter - między kodem a maszyną. I, jak zawsze w przypadku komputera, więcej oznacza więcej zasobów. Tłumacze byli wolniejsi, ponieważ musieli wykonywać więcej operacji.
Ostatnio widzieliśmy więcej języków hybrydowych, takich jak Java, które wykorzystują zarówno kompilator, jak i interpreter, aby działały. Jest to skomplikowane, ale JVM jest szybszy, bardziej wyrafinowany i znacznie bardziej zoptymalizowany niż stare interpretery, więc oznacza znacznie lepszą zmianę działania (z biegiem czasu) bliżej tylko prosto skompilowanego kodu. Oczywiście nowsze kompilatory mają również więcej fantazyjnych sztuczek optymalizacyjnych, więc mają tendencję do generowania znacznie lepszego kodu niż kiedyś. Ale większość optymalizacji, najczęściej (choć nie zawsze), powoduje pewien rodzaj kompromisu, tak że nie zawsze są one szybsze we wszystkich okolicznościach. Jak wszystko inne, nic nie przychodzi za darmo, więc optymalizatorzy muszą skądś się pochwalić (chociaż często wykorzystuje to procesor czasu kompilacji, aby zaoszczędzić czas pracy procesora).
Wracając do C, jest to prosty język, który można skompilować w dość zoptymalizowany zestaw, a następnie uruchomić bezpośrednio na maszynie docelowej. W C, jeśli zwiększysz liczbę całkowitą, jest więcej niż prawdopodobne, że jest to tylko jeden krok asemblera w procesorze, jednak w Javie może to być znacznie więcej (i może obejmować również trochę wyrzucania elementów bezużytecznych): -) C oferuje abstrakcję, która jest znacznie bliższa maszynie (asembler jest najbliższy), ale ostatecznie musisz wykonać znacznie więcej pracy, aby go uruchomić i nie jest tak chroniony, łatwy w użyciu lub przyjazny dla błędów. Większość innych języków zapewnia większą abstrakcję i dba o więcej podstawowych informacji, ale w zamian za ich zaawansowaną funkcjonalność wymagają więcej zasobów do działania. W miarę uogólniania niektórych rozwiązań musisz obsługiwać szerszy zakres komputerów,
Paweł.
źródło
++i
można skompilować do „add [ebp - 8], 1”. Nie wspominając już o tym, że pobieranie, przyrost, przechowywanie wciąż się nie dzieje, ale procesor zajmuje się tym i jest to tylko jedna instrukcja, jak powiedział Paul.Pomijając zaawansowane techniki optymalizacji, takie jak optymalizacja „ gorących punktów” , wstępnie skompilowane meta-algorytmy i różne formy paralelizmu , podstawowa szybkość języka silnie koreluje z ukrytą złożonością za kulisami wymaganymi do obsługi operacji, które zwykle być określone w wewnętrznych pętlach .
Być może najbardziej oczywistym jest sprawdzanie poprawności pośrednich odwołań do pamięci - takich jak sprawdzanie wskaźników
null
i sprawdzanie indeksów względem granic tablicy. Większość języków wysokiego poziomu wykonuje te kontrole niejawnie, ale C nie. Jednak niekoniecznie jest to fundamentalne ograniczenie tych innych języków - wystarczająco sprytny kompilator może być w stanie usunąć te kontrole z wewnętrznych pętli algorytmu poprzez jakąś formę ruchu kodu niezmiennego w pętli .Bardziej fundamentalną zaletą C (iw podobnym stopniu blisko spokrewnionego C ++) jest silne poleganie na alokacji pamięci opartej na stosie , która z natury jest szybka w alokacji, dezalokacji i dostępie. W C (i C ++) główny stos wywołań może być używany do alokacji prymitywów, tablic i agregatów (
struct
/class
).Podczas gdy C oferuje możliwość dynamicznej alokacji pamięci o dowolnym rozmiarze i czasie życia (przy użyciu tak zwanej „sterty”), domyślnie unika się tego (zamiast tego stosuje się stos).
Kuszące jest, że czasami można zreplikować strategię alokacji pamięci C w środowiskach wykonawczych innych języków programowania. Zostało to zademonstrowane przez asm.js , który pozwala na przetłumaczenie kodu napisanego w C lub C ++ na podzbiór JavaScript i bezpieczne działanie w środowisku przeglądarki internetowej - z niemal natywną prędkością.
Na marginesie, innym obszarem, w którym C i C ++ przewyższają większość innych języków pod względem szybkości, jest możliwość płynnej integracji z natywnymi zestawami instrukcji maszynowych. Godnym uwagi przykładem jest (zależna od kompilatora i platformy) dostępność funkcji SIMD, które wspierają konstrukcję niestandardowych algorytmów, które wykorzystują obecnie niemal wszechobecny sprzęt do przetwarzania równoległego - przy jednoczesnym wykorzystaniu abstrakcji alokacji danych zapewnianej przez język (niższy -poziomem przydziału rejestru zarządza kompilator).
źródło
Znalazłem odpowiedź na link o tym, dlaczego niektóre języki są szybsze, a niektóre wolniejsze, mam nadzieję, że to wyjaśni więcej o tym, dlaczego C lub C ++ jest szybszy niż inne. Istnieją również inne języki, które są szybsze niż C, ale nie możemy użyj ich wszystkich. Niektóre wyjaśnienia -
Jednym z głównych powodów, dla których Fortran pozostaje ważny, jest to, że jest szybki: procedury zrywania liczb pisane w Fortranie są zwykle szybsze niż równoważne procedury napisane w większości innych języków. Języki konkurujące z Fortranem w tej przestrzeni - C i C ++ - są używane, ponieważ są konkurencyjne w tej wydajności.
Rodzi to pytanie: dlaczego? Co takiego jest w C ++ i Fortranie, co czyni je szybkimi i dlaczego przewyższają inne popularne języki, takie jak Java lub Python?
Tłumaczenie ustne a kompilacja Istnieje wiele sposobów kategoryzacji i definiowania języków programowania, zgodnie ze stylem programowania, który zachęcają i oferowanymi funkcjami. Jeśli chodzi o wydajność, największym rozróżnieniem są języki interpretowane i skompilowane.
Podział nie jest trudny; raczej jest spektrum. Z jednej strony mamy tradycyjne skompilowane języki, grupę obejmującą Fortran, C i C ++. W tych językach istnieje dyskretny etap kompilacji, który tłumaczy kod źródłowy programu na postać wykonywalną, z której może korzystać procesor.
Ten proces kompilacji składa się z kilku etapów. Kod źródłowy jest analizowany i analizowany. W tym momencie można wykryć podstawowe błędy kodowania, takie jak literówki i błędy ortograficzne. Analizowany kod służy do generowania reprezentacji w pamięci, która również może być używana do wykrywania błędów - tym razem błędów semantycznych, takich jak wywoływanie nieistniejących funkcji lub próba wykonywania operacji arytmetycznych na ciągach tekstowych.
Ta reprezentacja w pamięci jest następnie używana do sterowania generatorem kodu, częścią, która wytwarza kod wykonywalny. Optymalizacja kodu w celu poprawy wydajności generowanego kodu jest przeprowadzana w różnych momentach tego procesu: optymalizacje wysokiego poziomu mogą być przeprowadzane na reprezentacji kodu, a optymalizacje niższego poziomu są wykorzystywane na wyjściu generatora kodu.
Właściwie wykonanie kodu następuje później. Cały proces kompilacji jest po prostu używany do stworzenia czegoś, co można wykonać.
Na drugim końcu mamy tłumaczy. Interpretatory będą zawierać etap parsowania podobny do etapu kompilatora, ale jest on następnie wykorzystywany do kierowania bezpośrednim wykonaniem, a program jest uruchamiany natychmiast.
Najprostszy interpreter zawiera w sobie kod wykonywalny odpowiadający różnym funkcjom obsługiwanym przez język - będzie więc miał funkcje dodawania liczb, łączenia ciągów znaków, cokolwiek innego ma dany język. Podczas analizy kodu wyszukuje odpowiednią funkcję i wykonuje ją. Zmienne utworzone w programie będą przechowywane w pewnego rodzaju tabeli odnośników, która mapuje ich nazwy na dane.
Najbardziej ekstremalnym przykładem stylu interpretera jest coś w rodzaju pliku wsadowego lub skryptu powłoki. W tych językach kod wykonywalny często nie jest nawet wbudowany w sam interpreter, ale raczej w osobne, samodzielne programy.
Dlaczego to wpływa na wydajność? Zasadniczo każda warstwa pośrednia zmniejsza wydajność. Na przykład najszybszym sposobem dodania dwóch liczb jest posiadanie obu tych liczb w rejestrach w procesorze i użycie instrukcji dodawania procesora. To właśnie potrafią skompilowane programy; mogą umieszczać zmienne w rejestrach i korzystać z instrukcji procesora. Jednak w interpretowanych programach ten sam dodatek może wymagać dwóch odnośników w tabeli zmiennych w celu pobrania wartości do dodania, a następnie wywołania funkcji w celu wykonania dodania. Ta funkcja może bardzo dobrze korzystać z tej samej instrukcji procesora, której używa skompilowany program, aby wykonać rzeczywiste dodawanie, ale cała dodatkowa praca przed użyciem instrukcji może spowolnić proces.
Jeśli chcesz dowiedzieć się więcej, sprawdź Źródło
źródło
Niektóre algorytmy C ++ są szybsze niż C, a niektóre implementacje algorytmów lub wzorców projektowych w innych językach mogą być szybsze niż C.
Kiedy ludzie mówią, że C jest szybki, a następnie przechodzą do rozmowy o jakimś innym języku, zwykle używają wydajności C jako punktu odniesienia.
źródło
Dzięki nowoczesnym kompilatorom optymalizującym jest mało prawdopodobne, aby czysty program w C był o wiele szybszy niż skompilowany kod .net, jeśli w ogóle. Dzięki zwiększeniu produktywności, które zapewniają platformy takie jak .net, możesz robić rzeczy w ciągu dnia, który zwykle zajmował tygodnie lub miesiące w zwykłym C. W połączeniu z niskim kosztem sprzętu w porównaniu z wynagrodzeniem dewelopera, pisanie jest ZNACZNIE tańsze. rzeczy w języku wysokiego poziomu i rzucaj sprzęt przy dowolnej powolności.
Jeff i Joel rozmawiają o tym, że C jest językiem „prawdziwego programisty”, ponieważ w C. nie ma trzymania się za ręce. Musisz przydzielić własną pamięć, zwolnić tę pamięć, wykonać własne sprawdzenie granic itp. Nie ma czegoś takiego jako nowy obiekt (); Nie ma wyrzucania elementów bezużytecznych, klas, OOP, struktur encji, LINQ, właściwości, atrybutów, pól ani niczego podobnego. Musisz znać takie rzeczy, jak arytmetyka wskaźników i jak wyłuskać wskaźnik. A jeśli o to chodzi, wiedz i rozumiesz, czym jest wskaźnik. Musisz wiedzieć, co to jest ramka stosu i jaki jest wskaźnik instrukcji. Musisz znać model pamięci architektury procesora, nad którym pracujesz. Istnieje wiele niejawnego zrozumienia architektury mikrokomputera (zwyklemikrokomputer, nad którym pracujesz) podczas programowania w języku C, który po prostu nie występuje ani nie jest konieczny podczas programowania w języku C # lub Java. Wszystkie te informacje zostały rozładowane do programisty kompilatora (lub VM).
źródło
Różnica między automatycznym i ręcznym, języki wyższego poziomu są abstrakcjami w ten sposób zautomatyzowanymi. C / C ++ są ręcznie kontrolowane i obsługiwane, nawet kod sprawdzania błędów jest czasem pracą ręczną.
C i C ++ są również językami kompilowanymi, co oznacza, że żaden z nich nie działa wszędzie, języki te muszą być dostosowane do sprzętu, z którym pracujesz, dodając w ten sposób dodatkową warstwę gotcha. Chociaż jest to obecnie nieco dziwne, ponieważ kompilatory C / C ++ stają się coraz bardziej popularne na wszystkich platformach. Możesz wykonywać kompilacje między platformami. Nadal nie jest to sytuacja wszędzie uruchamiana, po prostu instruujesz kompilator A, aby kompilował z kompilatorem B ten sam kod innej architektury.
Języki dolnej linii C nie są łatwe do zrozumienia ani uzasadnienia, dlatego też nazywane są językami systemowymi. Wyszli przed wszystkimi tymi nonsensami na wysokim poziomie abstrakcji. Dlatego też nie są one używane do programowania stron internetowych. Po prostu nie nadają się do tego zadania, ich sposób rozwiązywania złożonych problemów, których nie można rozwiązać za pomocą konwencjonalnych narzędzi językowych.
To dlatego dostajesz szalone rzeczy, takie jak (mikrostruktury, sterowniki, fizyka kwantowa, gry AAA, systemy operacyjne), są rzeczy, do których C i C ++ są po prostu dobrze dostosowane. Najważniejsze obszary to szybkość i chrupanie liczb.
źródło
C jest szybki, ponieważ jest natywnie skompilowanym językiem niskiego poziomu. Ale C nie jest najszybszy. W Benchmark Recursive Fibonacciego pokazuje, że Rust, Kryształ, a Nim może być szybciej.
źródło
Istnieje wiele powodów, w tym:
źródło