Debat: Går C++ over sine bredder med nye funktioner?

C++ er blevet fyldt med så mange nye funktioner, at det ikke længere er det hurtige sprog, det var en gang, mener udvikler, der bygger systemer til hurtige børstransaktioner.

C++ har i årtier været det mest velansete programmeringssprog, når der virkelig skulle laves alvorlige programmer til alvorlige formål - og til mindre alvorlige formål, for spiludviklere har også brugt C++. Valget af C++ har nemlig kunnet give udviklere muligheden for at skrive software, der kørte hurtigt.

Men i dag kniber det med hastigheden i C++. Det mener i hvert fald Henrique Bucher, som har skrevet et længere debatindlæg, hvori han kritiserer den udvikling C++ har gennemgået i de sidste 10 år.

Henrique Bucher har tidligere arbejdet inden for scientific computing og arbejder i dag med systemer til high frequency trading - altså de aktiehandelssystemer, hvor mikrosekunder er kostbare.

Det vil traditionelt være et område, hvor C++ ville være det oplagte valg, men sådan er det ikke i dag, fordi C++ efter Henrique Buchers mening er blevet overlæsset med en masse ekstra funktioner, der har skullet gøre sproget i stand til at se relevant ud, når det blev sammenlignet med nyere sprog.

Fokuser på hastighed

I stedet for at have tilføjet populære ting som closures til C++, så burde standardiseringsgrupperne bag C++11 og frem have holdt fokus på hastigheden, mener Henrique Bucher.

Hans interesseområde er de ekstremt krævende applikationer, hvor det i stedet er FPGA'er, som ganske vist er mindre ligetil at programmere, men hvor udbyttet til gengæld er større, som får denne niches opmærksomhed.

Spørger man andre steder, så bliver C++ i stedet trukket frem som et sprog, der i dag kan bruges til at udvikle applikationer på tværs af flere platforme end noget andet. Det mener eksempelvis produktchef John Thomas fra Embarcadero Technologies i denne artikel fra InfoWorld.

C++ kan i dag bruges til applikationer til Windows, Android, Linux, Mac OS X, Unix og adskillige andre platforme. Dermed ikke sagt, at det er det optimale valg til at udvikle på tværs af platformene, men muligheden er der.

Hvad mener du? Har C++ spredt sig for vidt og burde tilbage til rødderne - eller er det en udvikling, der tværtimod har gjort C++ mere relevant, fordi det kan bruges til mere?

Tips og korrekturforslag til denne historie sendes til tip@version2.dk
Kommentarer (9)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
#3 Allan Jensen

Hvis de nye funktioner ikke hjælper dig, så lad være med at bruge dem!?

Sproget bliver ikke langsommere af ny syntaks. De nye muligheder har absolute ingen indflydelse på effektiviteten af eksisterende kode. Så forsæt som du plejer og C++ vil aldrig blive langsommerer.

Men ikke at være et fjols er måske over hans programmerings-evner.

  • 4
  • 2
#4 Esben Nielsen

Jeg ser slet de nye template muligheder og lambda udtryk som problemer - de er blot en bonus.

Men man kan ikke fjerne er fundamentale compiler-udvidelser. Her tænker jeg på eksplicit tråd håndtering, hvor memory-modellen fra C++11 eksplicit skal understøtte multi-trådet programmering. Jeg ved det faktisk ikke, men jeg frygter, at compileren nu får frataget nogle optimeringsmuligheder af den grund.

Og jeg kan se, at man rundt omkring i standeard library får slæbt en masse atomic operationer med ind i kode, som enten er single-threaded, eller allerede tråd-beskyttet med eksplicitte låse. Det kan virkeligt sløve kode ned i multi-CPU maskiner.

Jeg frygter, at man har hoppet med på "multikerne" bølgen, uden at tænke på, at dem, som allerede har lavet super optimale C++ programmer til HPC, jo netop har gjort det med eksplicitte låse og optimeret dette og selv styret det udemærket. Nu presser man et multi-threading paradigme ned over folk, som ikke er optimeret til det givne program. Og man slipper jo ikke for låsene alligevel, nu er der bare en masse skjulte låse, som udvikleren ikke kan komme af med eller har kontrol over.

C++ (og C) burde i sit fundament være blevet single-threaded og så skulle folk eksplicit lave ting multithreaded ved hjælp af mutex eller, hvad nu man har lyst til - også uden at miste portabiliteten, da man jo udemærket kunne lave en standeard mutex og thread-safe varianter af ting i STL, man eksplicit kunne vælge til.

  • 3
  • 0
#5 Thorbjørn Martsum

Linus Thorvalds har udtrykt ret tydeligt et par gange hvad C++ er for en størrelse

Det Linus skriver virker umiddelbart til at være en overfladisk, usaglig og meningsløs bashing (udover lige et par få ord om STL - vil man kunne bytte C++ og C ud med vilkårligt andre sprog - og jeg vil med tilsvarende argumenter, kunne fremføre, at C er ulækkert, og assembler er det eneste rigtige ...

Mener du at man får et bedre sprog, hvis man tager C++ og fjerner templates, virtuelle funktioner osv? Men en udmelding som din, er jeg seriøst i tvivl om man får nok undervisning i sprog som C++ på universitet ...

For at skifte til selve tilføjelserne skete de fleste jo i C++11 - og det er da på sin vis bekymrende, at C++ bliver sværere at lære (og det er jo ikke et helt let sprog) - men de fleste af tilføjelserne er jo kommet af en årsag - og samtidig vil jeg ikke umiddelbart mene, at C++ er ene om at tilføje ny funktionalitet. Det virker som en trend - og alt andet en lige, så er C++ blevet bedre med C++11.

  • 2
  • 3
#6 Kevin Johansen

Du kan KUN skrive trådsikker C++ (præ 11), hvis du enten slår optimeringer fra eller skriver direkte til chipsæt. C++ compileren kender ike til mutex eller libs som pthreads og vil derfor IKKE overholde en lås el. lign. Læs evt. Hans Boehm (IBM engang) fra dengang de arbejdede mod c++99. Multithreading tilføjelserne er 100% nødvendige hvis sproget skal være nyttigt.

  • 2
  • 0
#7 Esben Nielsen

Sjovt nok har man kodet C++ og C multitrådet i årevis. Jeg er klar over, at kompileren skal have et minimum kendskab til mutex og andre atomiske operationer for ikke at lave uheldige optimeringer hen over disse kald. Normalt er et normalt OS mutex lock/unlock kald blevet anset som en tilstrække barriere. Jeg har svært ved at se, at en compiler kan lave "ulovlige" optimeringer hen over bibliotekskald, da den jo selv i et single-trådet program jo ikke kan have nogen anelse om, hvad der foregår nede i funktionen, og dermed ikke ved, hvad der er pillet i i memory. Hvis man kan finde lovlige optimeringer, som ødelægger en mutex, skal der selvfølgelig være en form for "barrier" håndtag, så ham som implementere en mutex kan bruge til at for at få compileren til at stoppe med ulovlige optimeringer hen over mutex lock - ligesom man har instruktioner som forhindre re-ordering pga. cache.

Det er da også fint man har adgang til en portable mutex, atomic og barriers i STL. Det er også ok at lave nogle nye standeard klasser og metoder i STL, som udnytter disse, til at lave thread-safe containere, eller hvad man nu ønsker.

Der, hvor det går galt er, hvis compileren eller STL selv sætte sådanne barriers eller atomic operationer ind. Det sker f.eks. i shared_ptr<>, som er gjort thread safe. Traditionelt beskytter man sine data - herunder en reference count - med en mutex. Når man nu alligevel har tråd-beskyttet sine data-strukture - hvilket man stadig skal i C++11 - er der jo ingen grund til de ekstra atomiske instruktioner. Igen er det fint at tilbyde en variant af shared_ptr, som er thread-safe, således ejerskab af en pointer kan deles over flere tråde, men da man alligevel som udvikler skal have fuldstændig styr på sine låse, ser jeg ingen grund til, at compiler/STL lige pludselig antager, at man ikke har.

Meget af dette kommer med ud fra den forfejlede antagelse af den gennemsnitlige koder skal kode multi-threaded. Det skal han ikke. Der er 2 gode grunde og een dårlig grund til, at vi overhoveder koder i multi-threaded:

1) Vi skal bruge flere kerner. Det er har de færreste brug for, og når de forsøger går performance forbedringerne lige ud med de ekstra låse der påkræves. Der er generelt kun en god mode at gøre det på: Kør hver sin beregning i hver sin tråd uden reelt at dele hukommelse (andet en konstant data). Og så skal compileren/STL ikke sætte ekstra barrierer/atomic ind.

2) Latency krav, som gør man ikke kan vente på andet tungt kode kører færdigt. Det er typisk i real-tidssystemer - men også i GUI programmer (hvem laver GUI i C++ nu om dage??). Igen, det simpleste er helt at undgå data-deling ved brug besked-køer. Og hvis man skal dele datastrukture, er en mutex (med priority inheritance i real-tidssystemer) en rimelig god løsning, men man skal holde tungen lige i munden for at sikre sig, at de tunge dele af koden slipper låsen, når de kører. Resultaterne skal så afleveres til de andre tråde via en lås - så det kan komme til at minde lidt om en beskedkø alligevel.

(For at løse 1) og 2) kunne man gå så vidt at overveje at bruge forskellige OS processer frem for shared memory, hvis OS tillader det, og så behøver man heller ikke compiler-understøttelse...)

3) (den dårlige grund) Blokerende API'er. Java var i starten bygger op så man skulle have en tråd til read() og een til write() - og så lige een til en timer. Desværre er der rigtigt mange softwareudviklere, der har lært dette paradigme. Det performer af h til. Brug select eller lignende. Node.js er meget effektiv, da den jo netop bruger et event library og ikke blokere på f.eks. accept() i en hovedtråd.

Jeg vil derfor komme med den påstand, at man som udvikler kan (og bør) kode reelt single-trådet ved enten at nøjes med én tråd eller at dele applikationen op i uafhængige tråde eller endda OS processer. Inde i disse blokke skal compiler/STL antage og optimere til, at man er effektivt single-treaded. Kun sjældent - og der skal udvikleren være eksplicit - flytter man data på tværs af tråde. Ellers opnår du et program, som er svært at overskue og sjældent performer særligt godt.

  • 1
  • 1
#9 Sune Marcher

Sjovt nok har man kodet C++ og C multitrådet i årevis.

Og man har debugget obskure runtime problemer lige så længe.

Det er til godt at der endeligt blev defineret en memory- og threading model, det er et skridt i den rigtige retning i forhold til at få fjernet undefined behavior. Selv med C++11 er det desværre stadig svært at skrive korrekt portabel C++. Og her taler jeg ikke engang om cross-platform portability, men blot at skifte mellem forskellige compilere til samme arkitektur, eller til en nyere compiler-version.

Jeg har ikke stødt på nogle steder hvor der er blevet introduceret unødvendige atomics eller locks til eksisterende STL, men hvis du har konkrete eksempler eller referencer til standarden ville det være interessant at høre. Og du kan ikke rigtigt tale om at std::shared_ptr "er gjort thread safe" - den har ikke været en officiel del af C++ før C++11, hvor memory modellen blev introduceret. Derudover giver det på ingen måde mening at thread-safety for smartptrs ikke skulle ligge internt i klassen, og endeligt er det næppe sync omkring de nye smartptrs der giver dig performance problemer - så har du i hvert fald en ret funky kodebase.

Memory/thread modellen, std::atomic, at volatile endeligt har en fornuftig definition (og så videre) gør at det nu cirka er muligt at skrive veldefineret kode der er både korrekt og udnytter din hardware fuldt ud - punktum.

Du har fuldstændigt ret i at de fleste udviklere bør ikke skrive kode på dét niveau, men det er nødvendigt for at library designers kan skrive effektive abstraktioner som resten kan bruge - samt de få der både har brug for, og er i stand til at skrive, den slags kode til specifikke use cases.

Herb Sutter har nogle ganske udmærkede videoer og blogindlæg med fokus på hvad memory model m.m. har af betydning for korrekthed, performance, og hvordan du skriver moderne C++ - jeg har indtil videre kun set fordele. Under læsning af Scott Meyers Effective Modern C++ er der dog nogle gange hvor jeg har tænkt "oh my god, they went too far" - hvor syntax har været horribel eller APIer har været klodsede.

  • 0
  • 0
Log ind eller Opret konto for at kommentere