Bjarne Stroustrup: Der er stadig ikke noget hurtigere end C++

C++ er fortsat et populært programmeringssprog, fordi det er bedst til de ting, der kræver ydelse. Andre sprog er gode til indpakning.

C++ er måske ikke det mest moderigtige programmeringssprog, men det ligger stadig i toppen blandt de mest udbredte sprog. Det er der en enkel forklaring på ifølge sprogets danske hovedophavsmand, Bjarne Stroustrup, skriver Infoworld.

»Der er ikke noget, som kan håndtere samme kompleksitet, der kan køre lige så hurtigt som C++. Du finder det ikke i apps og den slags, men derimod hos Googles søgemaskine eller Amazon, hvor der virkelig er brug for ydelse,« siger Bjarne Stroustrup til Infoworld.

Dermed er det dog ikke ensbetydende med, at andre programmeringssprog ikke spiller en væsentlig rolle.

»C++ er designet til temmelig hardcore applikationer, og det er altid blevet brugt sammen med én eller anden form for scriptsprog. Da jeg begyndte, brugte jeg C++ til alt, der krævede et rigtigt programmeringssprog og rigtig ydelse. Og så brugte jeg Unix shell som mit script-sprog. Sådan gjorde vi, og sådan gør man også i de fleste tilfælde i dag,« forklarer Bjarne Stroustrup.

I det fulde interview med Infoworld forklarer Bjarne Stroustrup også, at C++ er i færd med at blive forbedret over de seneste år for at give udviklere flere redskaber til at skrive bedre kode.

Tips og korrekturforslag til denne historie sendes til tip@version2.dk
Kommentarer (59)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Torben Mogensen Blogger

Jeg vil tro, at assemblerkode er hurtigere end C++. Det kræver en del arbejde at skrive programmer i assembler, men det kræver også en del arbejde at skrive effektive (og samtidigt korrekte) programmer i C++.

At C++ compilere kan generere rimelig effektiv kode skyldes også i høj grad, at der er brugt ekstremt mange mandtimer på at lave og fintune optimeringer i oversætterne. Hvis man brugte lige så mange mandetimer på at optimere f.eks. Standard ML, så ville programmer i dette sprog sagtens kunne konkurrere med C++ i effektivitet. Der er ikke noget i C++ som sådan, der gør det bedre ydende end andre sprog med 100% statiske typer. Tværtimod er der mange elementer i sproget, der gør det vanskeligt at lave optimeringer uden, at man risikerer at ændre semantikken af programmerne.

  • 15
  • 5
Christian Nobel

Det primære er vel egentlig hvor godt et job programmøren gør - en dårlig programmør kan sagtens lave noget der hoster af sted selv om han bruger C (whatever), samtidig med en god programmør kan lave noget i Visual Basic der fløjter over stok og sten.

Det er ret trættende at der jævnligt skal komme denne tissemandskonkurrence om hint eller dette sprog er bedre eller ringere, hurtigere eller langsommere, fuldstændig gammeldags, og hvad ved jeg, når det primære i virkeligheden er hvorvidt den enkelte er i stand til at løse den givne opgave på bedst mulig vis.

  • 14
  • 3
Morten Andersen

Assembler må jo altid være hurtiere, da asm-programmøren jo blot kan se hvad C++ compileren gjorde og så efterligne dette. ;)

C++'s hurtighed i mange sammenhænge skyldes dog også pointere. Pointere gør at man kan undgå at sende data men blot referencer rundt. Det kan man selvfølgelig også med referencer, men C++ har også pointer-aritmetik og man kan nemt caste en pointer mellem forskellige typer og umiddelbart arbejde videre med de samme data - f.eks. opfatte de samme data i memory både som char's, int's, short's m.m. alt efter hvad der lige måtte være optimalt i en given situation og uden noget overhead. Det kan være en stor fordel hvis man f.eks. modtager en datastruktur udefra og skal behandle dem. I andre programmeringssprog skal man typisk "parse" den og eksplicit konvertere elementerne til sprogets typer, mens man i C++ blot kan opfatte de enkelte indgange i strukturen som værende af bestemte typer og umiddelbart arbejde med dem. C++ har heller ikke range-checkingen m.m. på array tilgange, det øger også hastigheden (og fejlraten).

  • 5
  • 0
Albert Nielsen

Jeg vil tro, at assemblerkode er hurtigere end C++. Det kræver en del arbejde at skrive programmer i assembler ...

Der er ingen tvivl om, at velkomponeret assemblerkode afvikles langt hurtigere end alt andet end ren maskinkode. Compilere til alle andre sprog oversættes jo til ASM på vejen til maskinkoden. Jeg har i gamle dage programmeret meget i både ASM og maskinkode hos bl.a. Chr. Rovsing.

Hovedproblemet med ASM er, at højst 10% af programmørerne er i stand til at skrive ordentlig, vedligeholdelsesvenlig kode.

Kode jeg har set: JU .+172 - indsæt et enkelt statement mellem . og .+172 (OCT, DEC eller HEX???) og programmet dørker.

Real Programmers Don't Use Pascal :-)
http://www.pbm.com/~lindahl/real.programmers.html

  • 2
  • 1
Thomas Søndergaard

Jeg vil tro, at assemblerkode er hurtigere end C++. Det kræver en del arbejde at skrive programmer i assembler, men det kræver også en del arbejde at skrive effektive (og samtidigt korrekte) programmer i C++.

Det er selvfølgelig rigtigt at du kan skrive mindst lige så effektiv assembler, som compileren kan - Hvis du er rigtig rigtig dygtig. Men der går ikke ret mange år før din håndoptimerede assemblerkode ikke længere er så hurtig som det compileren genererer. C++ udvikleren beder bare sin compiler om at generere kode til i7 med -march=<fin-ny-cpu-type>, mens du kan starte forfra med din assembler :-)

Vedr indsatsen for at skrive effektive og korrekte C++ programmer, så er den C++ din far kendte kun et levn. C++11 kode ser markant anerledes ud end ældre C++ kode.

Vh Thomas

  • 9
  • 0
Bjarke Walling

Når vi snakker ydelse, så er der nogle algoritmer, som bliver skrevet til GPU'er i enten CUDA eller OpenCL. Alt afhængig af opgaven, så kan de performe væsentlig bedre end programmer skrevet i C++ til CPU'er.

  • 0
  • 4
Peter Stricker

Hovedproblemet med ASM er, at højst 10% af programmørerne er i stand til at skrive ordentlig, vedligeholdelsesvenlig kode.


Tjah, så må man jo bare lede lidt længere efter de rette folk. Det er jo ikke fordi, der er en umættelig mangel på assemblerprogrammører.

Et langt større problem er, at det er meget platformsspecifikt. Sproge(t/ne) er meget bundet op på den enkelte processorseries maskinkode instruktionssæt, og derudover er der store variationer i mnemonics og forskelle i rækkefølgen af register- og adresseparametre.

Hvis det program man laver kun skal afvikles på en enkelt arkitektur og hvis man som programmør kun laver programmer til en enkelt arkitektur, så er det selvfølgelig ligemeget.

  • 1
  • 1
Troels Henriksen

Jeg vil tro, at assemblerkode er hurtigere end C++. Det kræver en del arbejde at skrive programmer i assembler, men det kræver også en del arbejde at skrive effektive (og samtidigt korrekte) programmer i C++.

Bemærk at Stroustrup eksplicit betinger sin udtalelse med, at han kun sammenligner med sprog der har samme evne til at håndtere kompleksitet. Assembler har væsentligt dårligere abstraktionsevner end C++.

At C++ compilere kan generere rimelig effektiv kode skyldes også i høj grad, at der er brugt ekstremt mange mandtimer på at lave og fintune optimeringer i oversætterne.

Det tror jeg ikke er rigtigt - faktisk er C++-oversættere notorisk dårlige ift. f.eks. Fortran-oversættere eller gode C-oversættere. Specifikt, så mangler oversætteren typisk evnen til at lave gode optimeringer på tværs af de abstraktioner programmøren har designet. Til gengæld er det så her C++'s helt store og (næsten) unikke styrke træder i kraft: At man kan lave abstraktioner med meget lavt (eller intet) overhead. Det er en manuel proces, da man ikke kan regne med gode optimeringer fra oversætteren, men man kan til gengæld regne med ikke at have flere køretidsomkostninger end der er eksplicit i koden. Dette er ikke tilfældet i mere sofistikerede sprog, især de funktionelle.

Jeg tror årsagen er, at den virtuelle maskine man programmerer imod i C++ langt tættere på den fysiske maskine, så det er nemmere at vurdere hvorvidt ens kode vil være hurtig. I Haskell, f.eks., programmerer man i højere grad op mod hvilke optimeringer man ved ens oversætter kan udføre, hvilket kan gøre det mindre overskueligt.

Når vi snakker ydelse, så er der nogle algoritmer, som bliver skrevet til GPU'er i enten CUDA eller OpenCL. Alt afhængig af opgaven, så kan de performe væsentlig bedre end programmer skrevet i C++ til CPU'er.

OpenCL programmeres i en delmængde af C, og CUDA i en delmængde af C++. Jeg tror dog kun det er for vanens skyld, for programmeringsmodellen er helt anderledes, og de er ikke udpræget velegnede til formålet.

  • 9
  • 0
Hans Schou

Jeg vil tro, at assemblerkode er hurtigere end C++.


Hvis det ikke er målt, så er assembler langsommere (min påstand).

Nu kender jeg kun g++'s assembler, men den er ret "grim". Jo jo, man kan da sagtens genkende den kode man selv har skrevet, men det er noget værre rod, og typisk hurtigere i eksekvering end den vedligeholdelses-duelige kode man ville have skrevet i assembler.

Hvis man har en bit intensiv funktion som man har målt til at tage for meget tid, er det da værd og se på den oversatte assembler-source om den kan forbedres. Men måske får man så bare en idé til hvordan det kan skrives i C, så det bliver som man selv ville have skrevet det i assembler.

  1. Når man koder i assembler, laver man for mange fejl.
  2. Når man koder i C, laver man for mange fejl.
  3. Når man koder i C++, laver man for mange fejl.
  4. Kod i noget på et højere niveau, hvis muligt. Ellers tag forgående sprog.
  • 7
  • 1
Lars Skovlund

Der er specielle situationer hvor man kan have gavn af assembler (fx udnyttelse af specielle features i processorens instruktionssæt). Men hvis formålet er hastighed, må man efterprøve empirisk, at det man har knaldet sammen kører hurtigere. Ellers er det spildt arbejde, både nu og fremadrettet.

  • 2
  • 0
Troels Henriksen

Et langt større problem er, at det er meget platformsspecifikt. Sproge(t/ne) er meget bundet op på den enkelte processorseries maskinkode instruktionssæt, og derudover er der store variationer i mnemonics og forskelle i rækkefølgen af register- og adresseparametre.

Moderne assemblersprog er faktisk endnu mere følsomme: Da moderne processorer typisk dekoder deres "offentlige" assemblersprog til et enklere, RISC-lignende sprogt internt, så kan det variere fra generation til generation hvor lang tid det tager at afvikle en given instruktion. Endvidere skal man være intelligent omkring rækkefølgen af instruktioner, så man ikke skaber huller i processorens pipeline. Endelig bør man forsøge at bruge hukommelses-prefetch vektorinstruktioner (SSE og venner på x86) for at få optimal ydelse. Disse er netop ting som kræver beregninger og analyse, som det er meget svært for mennesker at gennemskue, for slet ikke at snakke om at koden bliver ulæselig, hvorfor det er en bedre idé at lade en oversætter tage sig af det.

  • 8
  • 1
Ole Laursen

Hvis man brugte lige så mange mandetimer på at optimere f.eks. Standard ML, så ville programmer i dette sprog sagtens kunne konkurrere med C++ i effektivitet. Der er ikke noget i C++ som sådan, der gør det bedre ydende end andre sprog med 100% statiske typer.

Jeg tror årsagen er, at den virtuelle maskine man programmerer imod i C++ langt tættere på den fysiske maskine, så det er nemmere at vurdere hvorvidt ens kode vil være hurtig.

Nemlig! Fakta er at håndtering af data i C og C++ med pointere er supereffektivt. At tale om compilere her er en helt forkert betragtning, problemet er at de algoritmer man naturligt skriver i SML (med rekursion og konstruktion/dekonstruktion af lister/træer) simpelthen ikke er særligt effektive. En god oversætter kan måske fjerne noget af boxing/unboxing-overheadet, men der skal virkelig en intelligent oversætter til at lave en rekursiv kædet liste om til et array med pointer-aritmetik.

Det opvejer i langt de fleste tilfælde dårlige aliasing-regler.

  • 3
  • 0
Jesper Louis Andersen

Nemlig! Fakta er at håndtering af data i C og C++ med pointere er supereffektivt. At tale om compilere her er en helt forkert betragtning, problemet er at de algoritmer man naturligt skriver i SML (med rekursion og konstruktion/dekonstruktion af lister/træer) simpelthen ikke er særligt effektive. En god oversætter kan måske fjerne noget af boxing/unboxing-overheadet, men der skal virkelig en intelligent oversætter til at lave en rekursiv kædet liste om til et array med pointer-aritmetik.

SML benytter også pointere til at håndtere data internt, og SML har også fin support for monomorfe arrays (Fordi SML kan lave imperativ kode og Standard-biblioteket indeholder monomorfe arrays). Det er ikke der problemet gemmer sig.

Problemet er en ideologisk forskel som Troels beskriver: I C++ har du eksplicit kontrol med dit data layout. I SML vælges det for dig. Det trade-off som der følger er at det er nemmere at skrive ML end C++, men den manglende kontrol kan i visse tilfælde koste grusomt i eksekveringshastighed.

  • 2
  • 0
Troels Henriksen

Problemet er en ideologisk forskel som Troels beskriver: I C++ har du eksplicit kontrol med dit data layout. I SML vælges det for dig. Det trade-off som der følger er at det er nemmere at skrive ML end C++, men den manglende kontrol kan i visse tilfælde koste grusomt i eksekveringshastighed.

Denne kontrol kan også gå den modsatte vej. På visse platforme og i visse situationer, da kan det være mere effektivt at bruge en struct af arrays frem for et array af structs (i semi-ML, ([a],[b]) versus [(a,b)]), grundet bedre alignment, mere regelmæssig hukommelsestilgang, og mulighed for vektorisering. En C++-oversætter har ikke en jordisk chance for at lave denne type transformation, hvorimod det er nogenlunde overkommeligt i mere højniveau-sprog. Dette er især af kritisk betydning på GPUer.

At udnytte de optimeringsmuligheder, man får ud af nøjniveau-sprog, kræver naturligvis en af disse tilstrækkeligt intelligente oversættere, og de kan desværre være lidt vanskelige at få fat på...

  • 2
  • 0
Henrik Tietgen

Når nu tråden synger på sidste vers, så tillader jeg mig lige at spørge lidt off-topic, mens de rette læsere er til stede.

Jeg har i mange år gerne ville igang med C++ og har også været startet en del gang. Primært via dvs tutorials på nettet. Men fælles for dem er typisk at de meget snævre,nok fordi de er lavet "on the run" som om man lige kommer i tanke om forskellige emner og derved gemmer en masse andet, uden en egentlig drejebog.

Nogen der kan linke til et site eller andet, som byder på en så komplet 'pakke' som mulig?

Som udgangspumkt er jeg ikke fan af amerikanske lærebøger, da her er så massivt meget tekst om næsten ingenting.

Nogen anbefalinger??

Henrik

  • 0
  • 0
Troels Henriksen

Men hvad udvikler d'herrer, siden denne voldsomme fokus på det jeg vil kalde superoptimering på lav niveau?

Jeg er PhD-studerende med fokus på optimerende oversættere. Min interesse for ydelse er derfor lidt a priori, og i langt de fleste tilfælde er det da heller ikke vigtigt. Hvis man udvikler meget fundamentale biblioteker (BLAS-implementeringer, grafikmotorer, kryptografi, osv), så kan det dog godt være besværet værd at kigge på hukommelsesoptimeringer. Min egen interesse er dog at lære en oversætter at udføre disse optimeringer, så mennesker ikke selv skal.

Indenfor high-performance computing i almindelighed er det også værd at bekymre sig om, for her er hardwaren ofte dyrere end programmøren! Endvidere får man en konkurrencemæssig fordel hvis man kan håndtere større problemer end konkurrenterne.

  • 3
  • 0
Jesper Louis Andersen

Men hvad udvikler d'herrer, siden denne voldsomme fokus på det jeg vil kalde superoptimering på lav niveau?

Den type overvejelser kommer typisk i spil hvis du har:

  1. En meget lille CPU-bundet kerne
  2. Mulighed for at spare mange penge hvis kernen kører hurtigt. Med andre ord er maskinen dyrere end programmøren med en del faktorer
  3. Problemet er stadig "sweet spot" i den forstand at det ikke kan betale sig at producere en passende FPGA eller ASIC-løsning.

Alternativt handler det om (hård) realtid eller hukommelsesforbrug.

De fleste moderne systemer ligger ikke i nogen af de klasser.

  • 0
  • 0
Christian Nobel

Den type overvejelser kommer typisk i spil hvis du har:

En meget lille CPU-bundet kerne    
Mulighed for at spare mange penge hvis kernen kører hurtigt. Med andre ord er maskinen dyrere end programmøren med en del faktorer    
Problemet er stadig &quot;sweet spot&quot; i den forstand at det ikke kan betale sig at producere en passende FPGA eller ASIC-løsning.  

Alternativt handler det om (hård) realtid eller hukommelsesforbrug.

De fleste moderne systemer ligger ikke i nogen af de klasser.

Tak til Jesper og Troels (btw, hvad er meningen med tallene efter dine indlæg?).

Jeg kan sagtens se hvor det at optimere giver god mening, især hvis man eksempelvis laver sådan noget som Varnish eller IP-Tables, udvikler kernemoduler etc, etc, og jeg kan sagtens forstå Troels' tilgang til sagen, men mit spørgsmål var ligeså meget rettet mod "almindelige" programører som laver det jeg vil kalde almindelige anvendelsesprogrammer.

Mao, hver ting til sin tid.

  • 0
  • 0
Nikolaj Koch

Ydelse har da stor betydning i mange sammenhænge. F.eks. i cloud-miljøer hvor det har betydning for hvor mange instanser man skal bruge, hvilket jo kan oversættes direkte til kroner/øre.

I desktop applikationer har det måske ikke så stor betydning fordi man her blot optimerer indtil det giver en acceptabel performance på de maskiner man nu forventer målgruppen har. Og da selv de billigste CPU'er for flere år siden er ganske kraftfulde i.f.t. typiske applikationsopgaver, betyder det man typisk ikke behøver optimere så meget. Det betyder så også at mange af de applikationer der anvendes frådser gevaldigt med resourcerne (det samme gør iøvrigt gældende for RAM-forbrug m.m.). Det virker måske fint nok, men det har alligevel nogle omkostninger idet det nedsætter mulighederne for kørsel af opgaver i baggrunden, multitaskning, virtualisering, flere bruger på samme maskine m.m. Ligesom det sommetider kan have en vis værdi for brugeren at hastigheden er hurtigere end selv den "acceptable". Det forøger også strømforbruget at lave unødigt mange beregninger.

Da der ser ud til at være kraftig stagnation i forbedringerne indenfor CPU'er (indenfor samme prispunkt/strømforsøg) er eneste vej frem at optimere hvis vi ønsker at computerne skal lave mere. Forhåbentligt vil mange af de meget low-level optimeirnger finde vej til standardbiblioteker så alle får dem med "gratis" uden selv at skulle kaste sig ud i assembler-programmering på mange arkitekturer osv.

Endeligt har performance m.m. altid været et vigtigt fokus punkt for datalogien, om ikke andet så af historiske årsager da det her næsten altid var relevant pga. begrænsede resourcer. Derfor er det et punkt der er af intellektuel interesse og optager mange nørder :-)

  • 2
  • 0
Troels Henriksen

Problemet er, når almindelige anvendelsesprogrammer ophøjes til en status, som hverken opdragelsesgiver eller programmør havde forestillet sig :)

Det er jo altid en afvejning... manuel optimering tager tid, men hvad der er langt værre, så gør det som regel også koden sværere at læse. At finde en metode til optimering, der ikke kræver at man omskriver ens kode således at modularitet og abstraktion forsvinder, er et svært problem, som det er værd at prøve at løse. Jeg kan umiddelbart se to muligheder:

1) Skriv ydelseskritiske dele af programmet i et begrænset sprog, som det er muligt for en specialiseret oversætter at optimere godt. Vi har faktisk allerede eksempler på denne model, i form af SQL, shadersprog, CUDA/OpenCL, LINQ og deslige.

2) Inspireret af bevissystemer som Coq, hvor man har en interaktiv dialog med systemet, så kunne man foretage en "samtale" med oversætteren om hvordan kode kan rykkes rundt, hvilket ville resultere i et "optimeringsscript", der lagres ved siden af den oprindelige kildekode. Det ville dog kræve, at samtalen gentages hver gang den ydelseskritiske kode ændres væsentligt. Jeg har set denne metode brugt til bevisudvikling, men aldrig til optimering.

...men man kan i grunden nok komme ganske langt med optimeringer, der ikke gør koden svær at forstå, men blot er lidt fornuftig omkring algoritmisk kompleksitet og hukommelsesallokering. Det er i praksis klart de fleste vi ser.

  • 2
  • 0
Jesper Larsen

Problemet er, når almindelige anvendelsesprogrammer ophøjes til en status, som hverken opdragelsesgiver eller programmør havde forestillet sig :)

I mange højniveau-sprog kan man jo embedde/kalde mere lavniveau-sprog som C++ eller C. Hvis man har en frygt for (eller forhåbning om), at det vil blive nødvendigt at optimere dele af ens kode rigtig meget (udover hvad det valgte sprog kan optimeres til) kan man jo vælge et højniveau-sprog med god understøttelse af dette (f.eks. Python).

Flaskehalsene i ens kode vil jo som regel være lokaliseret til ret få kodelinjer. Og hvis man ser på ekstraomkostningerne ved at udvikle hele kodebasen i f.eks. C++ frem for Python skal man spare rigtig meget på en bedre performance før det kan betale sig. Og her skal man jo også tage højde for, at ens produkt formentlig kommer senere på markedet hvis man holder sig til et rent lavniveau-sprog. Det kan indenfor meget konkurrenceprægede områder koste rigtig meget.

  • 0
  • 0
Torben Mogensen Blogger

Det er fuldstændigt korrekt, at en dygtig programmør kan lave lynhurtige C++ programmer, hvis hun kender sin målmaskine, ide hun har næsten samme styr på datalayout og -placering som, hvis hun brugte assembler.

Men det er kun et mindretal af C++ programmører, der evner dette. De resterende magter ikke andet end arrays og structs/objekter med simple allokeringsmønstre. Og når de så endelig kommer ud i at skulle bruge mere avancerede strukturer, så laver de programmer med space leaks eller for tidlig frigørelse af data. Endvidere indsætter de en masse overflødige indirections og test (defensiv programmering), som gør programmet langsommere.

  • 1
  • 3
Lasse Lasse

Bjarne Stroustrup sagde engang, at selv et spædbarn kan finde ud af at flytte et objekt, hvorefter han tog sin microfon og flyttede den fra den ene hånd over i den anden. Barnet laver ikke først en kopi og sletter originalen.

I C++11 er det noget, som er utrolig komplekst og tidskrævende at få til at virke korrekt. Man skal sætte sig ind i bl.a. prvalue, xvalue, glvalue, rvalue og lvalue og meget omhyggeligt skrive en stor del boilerplate. I praksis når man det kun til at fungere optimalt for præcis det subset af anvendelse som man selv bruger, og ikke al generisk anvendelse.

Samtidig skal man holde øje med selve compilerens optimeringer (som går helt udenom sprogets semantik) med RVO og NRVO, som forsøger helt at eliminere ens flytning ("move").

Det er bare fantastisk rodet, og C++ ISO standarden er samtidig tvetydig og mangelfuld.

Udover ovenstående "perfect forwarding" har C++ et andet sjovt fundamentalt problem:

Vidste du, at en std::vector ikke kan shrinke?

std::vector<int> v;
for(size_t t = 0; t < 1000000000; t++)
v.push_back(123);

for(size_t t = 0; t < 1000000000; t++)
v.erase(v.begin() + t);

Efter sidste løkke vil v stadig fastholde 4 GB RAM. Fordi std::alloc kun har new og delete kan v kun shrinke ved at relokeres. Men da man jo kunne have oprettet iteratorer fra v (det kan compileren ikke gennemskue, om man har), så ville en relokering jo invalidere dem.

Dertil kommer aliasingproblemet i C++, hvor en compileren ikke kan vide, om en funktions argumenter f(T &a, T &b) overlapper eller ej (sproget er for komplekst til, at den kan gennemskue det). Det udelukker en del optimeringer.

Suk...

  • 3
  • 0
Torben Mogensen Blogger

Ja, der er mangel ting, der er ret bøvlede at skrive i C++. Lad os tage et simpelt eksempel: Lav en funktion, er tager et par af to værdier og returnerer et nyt par af de samme to værdier, men byttet om. Funktionen skal virke uanset typerne af de to værdier i parret, som endvidere kan være forskellige -- f.eks. kan det første element være et heltal og det andet en liste af tegn.

I SML er det blot

fun swap (x,y) = (y,x)

Lad os se den korteste, korrekte C++ definition af samme. Brug gerne std::pair klassen.

  • 0
  • 1
Lasse Lasse

Ikke hvis de er defineret som indekser, frem for hukommelsesværdier. Det er jeg faktisk ret sikker på, at de er, da du heller ikke ville kunne gro uden at invalidere iteratorer.

std::vector::iterator er en ren pointer og ikke andet (med mindre, man kører med checked iteratorer i debug mode).

Så både push_back() og erase() kan invalidere iteratorer.

  • 1
  • 0
Lasse Lasse

Funktionen skal virke uanset typerne af de to værdier i parret, som endvidere kan være forskellige -- f.eks. kan det første element være et heltal og det andet en liste af tegn.

Nu er C++ statisk typed, så det, du beskriver, ligger uden for C++ paradigmet, selvom det godt kunne lade sig gøre at lave noget hack.

Det er faktisk sjovere, at bede folk om at skrive den korteste version, som altid er så effektiv som muligt.

  • 1
  • 0
Troels Henriksen

Lav en funktion, er tager et par af to værdier og returnerer et nyt par af de samme to værdier, men byttet om.

template <typename A, typename B>  
std::pair<B,A> swap(std::pair<A,B> p) {  
  return std::make_pair(p.second, p.first);  
}

Det synes jeg ikke er noget særligt godt eksempel på kompleksiteten i C++. Den ekstra notation skyldes primært eksplicit angivelse af retur- og argument-typer (som man altså også bør gøre i funktionssprog), samt mangel på indbygget syntaks for tupler.

Rettelse: Jeg havde byttet om på elementtyperne i returtypen.

  • 1
  • 0
Troels Henriksen

Så både push_back() og erase() kan invalidere iteratorer.

Ifølge C++03-specifikationen, så må std::vector::erase() ikke invalidere iteratorer der ligger før det slettede område. std::vector::push_back() kan slet ikke invalidere iteratorer. Jeg tvivler alvorligt på at C++11 har brudt denne kontrakt.

Rettelse: Hov, vent, jeg tog fejl, push_back må faktisk godt invalidere hvis vektoren er nødt til at vokse. Det lader til, at man skal bruge resize-metoden hvis man virkelig vil sikre sig, at vektoren har den ønskede størrelse efter erase().

  • 0
  • 0
Lasse Lasse

std::vector::push_back() kan slet ikke invalidere iteratorer.

I C++11 standarden står der:

23.3.6.5 vector modifiers
void push_back(T&& x);
1
Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens,
all the iterators and references before the insertion point remain valid.

Dette er et pragteksempel på, at standarden er tvetydig (som jeg skrev i mit oprindelige indlæg). De siger ikke direkte, at iteratorer invalideres ved relokering, men antyder det ved at sige, at de ikke invalideres hvis der ikke relokeres.

Det, som standarden mener, er, at push_back() godt kan invalidere iteratorer. Det kan du selv teste i et ganske simpelt program, eller fx google "push_back invalidate iterators".

  • 0
  • 0
Jesper Louis Andersen

Ydelse har da stor betydning i mange sammenhænge. F.eks. i cloud-miljøer hvor det har betydning for hvor mange instanser man skal bruge, hvilket jo kan oversættes direkte til kroner/øre.

Ydelse har ganske rigtigt stor betydning i cloud-miljøer. Men bemærk at dit problem skal være CPU-bundet for at det hjælper med C++ eller lignende optimeringer. De fleste Cloud-systemer jeg har været med til at udvikle er IO-bundne og her er andre sprog, såsom Erlang, langt bedre værktøjer (Erlang er fortolket, men runtimen er højoptimeret C-kode og kan klare voldsomme belastninger). Det handler om at kunne mere med mindre instanser, men problemet handler mere om hvordan du flytter data rundt og hvor smart du er med at undgå unødvendig kopiering af data.

Problemet med C++ her er at du ender med at skulle genskrive 99% af Erlang-runtimen, og så er du ofte bedre stillet med Erlang i starten, for du kommer til at lave fejl og du kommer til at dumme dig en del.

  • 0
  • 0
Jesper Louis Andersen

Nu er C++ statisk typed, så det, du beskriver, ligger uden for C++ paradigmet, selvom det godt kunne lade sig gøre at lave noget hack.

Torbens Standard-ML eksempel:

fun swap (x, y) = (y, x)

...er også statisk typet:

quasit:~ jlouis$ poly  
Poly/ML 5.5.2 Release  
> fun swap (x,y) = (y, x);  
val swap = fn: 'a * 'b -> 'b * 'a

Men Standard-ML har typeinferens og regner selv ud at koden virker for vilkårlige typer 'a og 'b:

> swap (1, 3.0);  
val it = (3.0, 1): real * int  
> swap ("hello", ref 37);  
val it = (ref 37, "hello"): int ref * string

Velkommen til et sprog med et ordentligt typesystem :)

  • 1
  • 0
Mogens Hansen

Måske den bedste begynder lærebog, der findes.

Det er jeg meget enig i.

Bjarne Stroustrup har også 2 bøger som er værd at kigge på:
A Tour of C++
http://www.amazon.com/Tour-In-Depth-Series-Bjarne-Stroustrup/dp/03219583...-
4&keywords=bjarne+stroustrup
som henvender sig primært mod folk der har programmeret en del i forvejen, og

Programming: Princples and Pratice Using C++ (2nd Edition)
http://www.amazon.com/Programming-Principles-Practice-Using-Edition/dp/0...
som henvender sig mod folk med begrænset programmeringserfaring

kombiner det med en god, gratis C++ compiler til din platform, f.eks.
* Microsoft Visual C++ Express til Windows
* gcc eller Clang til Linux og Mac
og så er man godt igang

  • 1
  • 0
Lasse Lasse

Nu er C++14 blevet godkendt, og dit eksempel kan så skrives endnu enklere:

Det kan du også i C++11 med template argument deduction fremfor return type deduction:

template <class T> T swap(T p) {  
    return std::make_pair(p.second, p.first);  
}

edit: hov, det kan man ikke sådan, sorry :)

  • 0
  • 0
Michael Schade

Det er da klart, ju +- et eller andet offset, er et jump der er offset i forhold til den adresse som den du står på i det øjeblik du eksekverer instruktionen. Derfor bruger du symbolsk maskinkode til at angive destinationsadressen som så bliver løst når koden linkes, jump <offset> instruktionen er beregnet til små korte hop og er typisk effektiv i kraft af at den typisk ikke fylder mere end ét word....Word her brugt i sin oprindelige betydning af størrelsen på et (instruktions-)register. Dvs. instruktionen kunne loades og eksekveres i en mundfuld.

Re: Tissemandskonkurrencen, så er der stadig forskel på fortolkede og oversatte sprog og bare fordi vi får kraftigere hardware er der ingen grund til at smide om sig med tomme cykler.

I C/C++ kan man gennem at tænke sig om når man koder, udnytte den underliggende hw-arkitektur i en grad som selv den mest optimerende compiler aldrig nogensinde vil komme til, uanset om du sætter den til at optimere for hastighed, programstørrelse eller kombo'en, selv en optimeret ml compiler vil ikke komme tæt nok på. Ml tillader ikke direkte manipulering med adresser og pointere eller interruptstrukturer, timere osv., hvilket er essentielt for hw-nær optimering, der kan muligvis opnåes noget ved at tilknytte nogle eksterne lib-funktioner, men gæt hvad de nok er skrevet i.
Det samme dilemma som nutidens glue-code sprog lider af, man kan lave flot typesafe osv. kode, men skal man lave noget der kan noget så skal man bruge en biblioteksfunktion fra 'frameworket', som i bla. java er på over 4500++ funktioner (og der kommer hele tiden flere til), som man så interfacer til for på den måde ikke at komme i berøring med 'rigtig' kode.

  • 0
  • 0
Jesper Louis Andersen

Ml tillader ikke direkte manipulering med adresser og pointere eller interruptstrukturer, timere osv., hvilket er essentielt for hw-nær optimering, der kan muligvis opnåes noget ved at tilknytte nogle eksterne lib-funktioner, men gæt hvad de nok er skrevet i.

De er typisk skrevet i assembler eller C, alt efter hvad der er behov for. Det er som oftest bare en lille stub fordi prisen for at kalde C-kode i, say, OCaml er så lille at man bare gør det uden at tænke overhead. Ideen er så at skrive hele resten af driveren i et sprog der er Garbage-collected så man slipper for at overveje hukommelsessnask i størstedelen af programmet. Så man kan godt, hvis bare HW-bindingen er så low-level at det giver mening at skrive resten af det i et sprog af højere niveau.

Tag et kig på eksempeltvist MirageOS eller HalVM for denne tilgang. De har f.eks. hele TCP/IP stakken i OCaml i MirageOS. En sjov historie fra Anil Madhavapeddy's Phd er at da han skrev en SSH-klon i OCaml, så outperformede han C implementationen. Simpelthen fordi den højere abstraktion han havde tilgængelig lod ham strukturere programmet bedre. Programmering er ikke så sort/hvidt omkring hastighed :)

  • 1
  • 0
Troels Henriksen

I C/C++ kan man gennem at tænke sig om når man koder, udnytte den underliggende hw-arkitektur i en grad som selv den mest optimerende compiler aldrig nogensinde vil komme til

Det passer altså ikke helt på moderne hardware. C/C++ tillader dig at bestemme repræsentationen i hukommelsen, hvilket giver dig mange fordele, men eksponerer slet ikke moderne maskiners SIMD/vektor-parallelisme. For at udnytte SIMD skal ens data være placeret og repræsenteret på nogle ganske (arkitektur-)specifikke måde, som C/C++ ikke direkte eksponerer i sproget. Jeg har tidligere nævnt eksemplet med at arrays af structs ikke fungerer ydelsesmæssigt - det kan man selv hacke sig uden om, men du kan ikke sige at et array skal være aligned på en specifik måde uden at benytte skrøbelige sprogudvidelser.

Moderne hardware er virkelig svært at få optimal ydelse ud af, om du er en oversætter eller en C-programmør. Heldigvis er det så hurtigt, at selv sub-optimal ydelse stadigvæk er pænt hurtigt.

  • 0
  • 0
Baldur Norddahl

Go:

a, b = b, a

Tja:

a := 5  
b := "hello"  
a,b = b,a  
   
prog.go:11: cannot use b (type string) as type int in assignment  
prog.go:11: cannot use a (type int) as type string in assignment  
 [process exited with non-zero status]

Desuden blev du bedt om at lave en funktion der bytter om på en tuple. Det der er ikke det samme.

  • 0
  • 0
Peter Kobl

det er kun noget jeg har hørt om, men i nogle gamle (IBM?) arkitekturer kunne man "installere" egen mikrokode, altså et niveau under assembler/maskinkode. Og så operere på ALU'en, shifteren, scratch pad'en og sådan noget. I nogle apps, nok mest loops, skulle det have kunne det gi' et skub så tingene gik hurtigere end med assembler/maskinkode. Er der nogen der har prøvet det? Er det noget man kan gøre med nogle af de nu gængse arkitekturer?

jeg har ikke noget at bruge det til, men bare af interesse.

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