Wiem, że dość często słyszałem, że C zazwyczaj ma przewagę wydajności nad C ++. Tak naprawdę nie myślałem o niczym innym, dopóki nie zdałem sobie sprawy, że MSVC wydaje się nawet nie obsługiwać najnowszego standardu C, ale najnowszy obsługuje C99 (o ile mi wiadomo).
Planowałem napisać bibliotekę z kodem do renderowania w OpenGL, aby móc go ponownie użyć. Planowałem napisać bibliotekę w C, ponieważ wzrost wydajności jest mile widziany, jeśli chodzi o grafikę.
Ale czy naprawdę byłoby warto? Kod korzystający z biblioteki prawdopodobnie zostałby napisany w C ++, a ja wolę kodować ogólnie w C ++.
Jeśli jednak spowodowałoby to nawet niewielką różnicę w wydajności, prawdopodobnie wybrałbym C.
Można również zauważyć, że ta biblioteka byłaby czymś, co zrobiłbym do pracy w systemie Windows / OS X / Linux i prawdopodobnie skompilowałbym wszystko natywnie (MSVC dla Windows, Clang lub GCC dla OS X i GCC dla Linux .. . lub ewentualnie kompilatory Intela do wszystkiego).
Rozejrzałem się i znalazłem pewne testy porównawcze i takie, ale wszystko, co widziałem, dotyczyło GCC, a nie MSVC i Clanga. Ponadto testy porównawcze nie wspominają o standardach używanych języków. Ktoś ma jakieś przemyślenia na ten temat?
EDYTOWAĆ:Chciałem po prostu podzielić się moim poglądem na to pytanie po kilku latach doświadczenia. W końcu napisałem projekt, o który zadawałem to pytanie w C ++. Rozpocząłem inny projekt mniej więcej w tym samym czasie w C, ponieważ chcieliśmy uzyskać jak najmniejszą wydajność, którą mogliśmy i potrzebowaliśmy, aby projekt był możliwy do połączenia w C. Kilka miesięcy temu dotarłem do punktu, w którym naprawdę potrzebowałem map i zaawansowanych manipulacja sznurkiem. Wiedziałem o możliwościach tego w standardowej bibliotece C ++ i ostatecznie doszedłem do wniosku, że te struktury w standardowej bibliotece prawdopodobnie przewyższą i będą bardziej stabilne niż mapy i łańcuchy, które mógłbym zaimplementować w C w rozsądnym czasie. Wymóg bycia linkowalnym w C został łatwo spełniony przez napisanie interfejsu C do kodu C ++, co zostało zrobione szybko z nieprzezroczystymi typami. Przepisywanie biblioteki w C ++ wydawało się przebiegać znacznie szybciej niż podczas pisania w C i było mniej podatne na błędy, zwłaszcza wycieki pamięci. Byłem także w stanie korzystać ze standardowej biblioteki wątków bibliotecznych, co było znacznie łatwiejsze niż stosowanie specyficznych dla platformy implementacji. Ostatecznie uważam, że napisanie biblioteki w C ++ przyniosło ogromne korzyści przy możliwie niskim koszcie wydajności. Nie przetestowałem jeszcze wersji C ++, ale wierzę, że może być nawet możliwe, że osiągnąłem pewną wydajność, używając standardowych struktur danych biblioteki niż te, które napisałem. Wierzę, że pisanie biblioteki w C ++ przyniosło ogromne korzyści przy możliwie niskim koszcie wydajności. Nie przetestowałem jeszcze wersji C ++, ale wierzę, że może być nawet możliwe, że osiągnąłem pewną wydajność, używając standardowych struktur danych biblioteki niż te, które napisałem. Wierzę, że pisanie biblioteki w C ++ przyniosło ogromne korzyści przy możliwie niskim koszcie wydajności. Nie przetestowałem jeszcze wersji C ++, ale wierzę, że może być nawet możliwe, że osiągnąłem pewną wydajność, używając standardowych struktur danych biblioteki niż te, które napisałem.
Odpowiedzi:
Sądzę, że ludzie często twierdzą, że C jest szybszy niż C ++, ponieważ łatwiej jest uzasadnić wydajność w C. C ++ nie jest z natury wolniejszy ani szybszy, ale niektóre kody C ++ mogą ukrywać ukryte kary za wydajność. Na przykład mogą istnieć kopie i niejawne konwersje, które nie są natychmiast widoczne, gdy patrzymy na jakiś fragment kodu C ++.
Weźmy następujące oświadczenie:
Załóżmy dalej, że
doSomething
ma następujący podpis:Teraz spróbujmy przeanalizować możliwy wpływ tego konkretnego stwierdzenia na wydajność.
W C implikacje są dość jasne.
foo
może być tylko wskaźnikiem do struktury idoSomething
musi być wskaźnikiem do funkcji.*c
dereferencje długo ia + 5
jest dodawanie liczb całkowitych. Jedyna niepewność wynika z rodzajua
: Jeśli nie jest to int, nastąpi pewna konwersja. ale poza tym łatwo jest oszacować wpływ tego pojedynczego wyrażenia na wydajność.Teraz przejdźmy do C ++. To samo oświadczenie może teraz mieć bardzo różne cechy wydajności:
doSomething
może to być nie-wirtualna funkcja członka (tania), funkcja wirtualnego członka (nieco droższa)std::function
, lambda ... itd. Co gorsza,foo
może to być przeciążenie typu klasyoperator->
z pewną operacją o nieznanej złożoności. Aby więc obliczyć koszt połączeniadoSomething
, należy teraz znać dokładną naturęfoo
idoSomething
.a
może być liczbą całkowitą lub odniesieniem do liczby całkowitej (dodatkowa pośrednia) lub typem klasy, który implementujeoperator+(int)
. Operator może nawet zwrócić inny typ klasy, do którego domyślnie można przekształcićint
. Ponownie, koszt wydajności nie wynika z samego oświadczenia.c
może być implementacją typu klasyoperator*()
. Może to być również odniesienie dolong*
itp.Dostajesz obraz. Ze względu na C ++ 's funkcje języka, to jest o wiele trudniejsze do oszacowania kosztów wydajności pojedynczej instrukcji, niż jest to w C. Teraz w uzupełnieniu, abstrakcje jak
std::vector
,std::string
są powszechnie stosowane w C ++, które posiadają właściwości działania własne, i alokacji pamięci hide dynamiczne ( zobacz także odpowiedź @ Iana).Podsumowując: Ogólnie rzecz biorąc, nie ma różnicy w możliwej wydajności możliwej do osiągnięcia przy użyciu C lub C ++. Ale w przypadku kodu naprawdę krytycznego dla wydajności ludzie często wolą używać C, ponieważ istnieje znacznie mniej możliwych ukrytych kar za wydajność.
źródło
Kod napisany w C ++ może być szybszy niż w C, dla niektórych typów zadań.
Jeśli wolisz C ++, użyj C ++. Wszelkie problemy z wydajnością będą nieznaczne w porównaniu z algorytmicznymi decyzjami twojego oprogramowania.
źródło
Jedną z zasad projektowania C ++ jest to, że nie płacisz za funkcje, których nie używasz. Tak więc, jeśli napiszesz kod w C ++ i unikniesz funkcji, które nie istnieją w C, to wynikowy skompilowany kod powinien mieć równoważną wydajność (choć będziesz musiał to zmierzyć).
Korzystanie z klas jest na przykład niewielkie, w porównaniu do struktur i wielu powiązanych funkcji. Funkcje wirtualne będą kosztować nieco więcej i będziesz musiał zmierzyć wydajność, aby sprawdzić, czy ma to znaczenie dla Twojej aplikacji. To samo dotyczy każdej innej funkcji języka C ++.
źródło
this
funkcji języka wskaźnika. To wszystko co powiedziałem.Jednym z powodów, dla których języki wyższego poziomu są czasami wolniejsze, jest to, że mogą ukryć za kulisami znacznie więcej zarządzania pamięcią niż języki niższego poziomu.
Każdy język (lub biblioteka, API itp.), Który wyodrębnia szczegóły niskiego poziomu, może potencjalnie ukrywać kosztowne operacje. Na przykład w niektórych językach po prostu przycięcie końcowych białych znaków z ciągu powoduje przydzielenie pamięci i kopię ciągu. W szczególności przydzielanie pamięci i kopiowanie może stać się kosztowne, jeśli będą się powtarzać w ciasnej pętli.
Jeśli napiszesz tego rodzaju kod w C, byłoby to oczywiste oczywiste. W C ++ być może mniej, bo alokacje i kopiowanie można gdzieś wyodrębnić do klasy. Mogą być nawet ukryte za niewinnie wyglądającym przeciążonym operatorem lub konstruktorem kopii.
Więc użyj C ++, jeśli chcesz. Ale nie daj się zwieść pozornej wygodzie abstrakcji, gdy nie wiesz, co kryje się pod nimi.
Oczywiście użyj profilera, aby dowiedzieć się, co naprawdę spowalnia Twój kod.
źródło
Jeśli chodzi o to, co warto, zwykle piszę biblioteki w C ++ 11 dla rozszerzonego zestawu funkcji. Lubię być w stanie korzystać z takich rzeczy, jak wspólne wskaźniki, wyjątki, ogólne programowanie i inne funkcje tylko w C ++. Lubię C ++ 11, ponieważ zauważyłem, że spora jego część jest obsługiwana na wszystkich platformach, na których mi zależy. Visual Studio 2013 ma wiele podstawowych funkcji językowych i implementacji bibliotek gotowych do użycia i podobno pracuje nad dodaniem pozostałych. Jak dobrze wiesz, zarówno Clang, jak i GCC obsługują również cały zestaw funkcji.
Powiedziawszy to, niedawno przeczytałem o naprawdę świetnej strategii dotyczącej rozwoju biblioteki, która moim zdaniem jest bezpośrednio związana z twoim zapytaniem. Artykuł zatytułowany jest „Styl obsługi błędów AC, który dobrze się gra z wyjątkami C ++” Stefanu Du Toit określa tę strategię jako wzór „klepsydry”. Pierwszy akapit tego artykułu:
Teraz, aby rozwiązać Twój główny problem: wydajność.
Sugerowałbym, podobnie jak wiele innych odpowiedzi tutaj, że pisanie kodu w dowolnym języku działałoby równie dobrze z punktu widzenia wydajności. Z osobistego punktu widzenia pisanie poprawnego kodu w C ++ jest łatwiejsze ze względu na funkcje językowe, ale myślę, że to osobiste preferencje. Tak czy inaczej, kompilatory są naprawdę inteligentne i mają tendencję do pisania lepszego kodu niż ty. To znaczy, że kompilator prawdopodobnie zoptymalizuje Twój kod lepiej niż możesz.
Wiem, że wielu programistów tak mówi, ale pierwszą rzeczą, którą powinieneś zrobić, to napisać kod, a następnie profilować go i dokonywać optymalizacji tam, gdzie sugeruje to twój profiler. Twój czas będzie znacznie lepiej spędzony na tworzeniu funkcji, a następnie na ich optymalizacji, gdy zobaczysz, gdzie są twoje wąskie gardła.
Teraz kilka zabawnych lektur na temat tego, jak funkcje językowe i optymalizacje mogą naprawdę działać na twoją korzyść:
std :: unique_ptr ma zerowy narzut
constexp pozwala na obliczenia w czasie kompilacji
semantyka przenoszenia zapobiega niepotrzebnym obiektom tymczasowym
źródło
std::unique_ptr has zero overhead
Nie może to być prawdą (technicznie rzecz biorąc), ponieważ musi zostać wywołany jego konstruktor, jeśli stos rozwinie się z powodu wyjątku. Wskaźnik surowy nie ma tego narzutu i nadal jest poprawny, jeśli Twój kod prawdopodobnie nie wyrzuci. Kompilator nie będzie w stanie tego udowodnić w ogólnym przypadku.unique_ptr
? Jest zadeklarowanynoexcept
, a więc przynajmniej obsługuje wszystkie wyjątki, ale nie mogę sobie wyobrazić, jakiego rodzaju wyjątek mógłby zostać w ogóle rzucony.Różnica w wydajności między C ++ i C nie wynika z niczego w języku, mówiąc ściśle, ale z tego, co kusi cię do zrobienia. To jest jak karta kredytowa kontra gotówka. Nie powoduje to, że wydajesz więcej, ale i tak robisz, chyba że jesteś bardzo zdyscyplinowany.
Oto przykład programu napisanego w C ++, który został następnie agresywnie dostrojony. Musisz wiedzieć, jak wykonywać agresywne dostrajanie wydajności, niezależnie od języka. Metoda, której używam, to losowe wstrzymywanie, jak pokazano na tym filmie .
Kosztowne rzeczy, które kusi C ++, to nadmierne zarządzanie pamięcią, programowanie w stylu powiadomień, ufanie programowi w przeciwieństwie do wielowarstwowych bibliotek abstrakcji (jak powiedział @Ian), ukrywanie spowolnienia itp.
źródło
C nie ma żadnej przewagi wydajności w porównaniu do C ++, jeśli robisz to samo w obu językach. Możesz wziąć dowolny stary kod C napisany przez dowolnego porządnego programistę C i przekształcić go w prawidłowy i równoważny kod C ++, który będzie działał równie szybko (chyba że zarówno ty, jak i twój kompilator wiecie, co robi słowo kluczowe „ogranicz” i używacie go skutecznie, ale większość ludzi nie).
C ++ może mieć znacznie inną wydajność, wolniejszą lub szybszą, jeśli (1) używasz standardowej biblioteki C ++ do robienia rzeczy, które można wykonać znacznie szybciej i łatwiej bez korzystania z biblioteki, lub (2) jeśli używasz standardowej biblioteki C ++ robić rzeczy o wiele łatwiej i szybciej niż przez ponowną implementację biblioteki w złym C.
źródło