C++ och dess framtid att programmera minnessäkert - Hur går utvecklingen?

Permalänk
Medlem
Skrivet av Yoshman:

OK, så frågan kvarstår: kan du ge ett enda konkret exempel när det är viktigt att ha 32 bytes meta-data per nod jämfört med 48 bytes meta-data. Vad är det för typ av JSON-dokument man parsar då?

Spelar hastighet någon roll för dig?
Har du objekt som är 32 byte får du in två stycken i en cache line, jämfört med som med 48 byte då det bara får plats en så har du snabbare kod. En cache line är på x86 64 byte för att ta ett exempel. 48 byte är en dum siffra för datorer

Permalänk
Medlem
Skrivet av Ingetledigtnamn:

Videon avfärdade helt och hållet uttalandet från @klk om att man som utvecklare blir mer effektiv om man använder DOD. Om man måste använda hjärncykler till att analysera hur saker och ting bäst skall sparas i minne och vad som är den mest gynnsamma encoding man kan komma på borde det rimligen betyda att det tar längre tid att skriva kod för den som kör DOD jämfört med en traditionell OOP-design.

Tänk på att tagged unions är en ingenjörslösning, och vad menar jag med det?
Det är som att du direkt ser hur du kan dra nytta av den om du förstår vad det löser.

Om du läser kod och ser olika typer av lösningar på problem, då märker du själv hur de löst problemen om du förstår lösningen. Förstår du inte lösningen förstår du heller inte hur du eventuellt kan använda den.

Ta ett om du exempelvis skall skriva en parser för programmeringsspråk och då den aktuella videon diskuterar parsning av språk, då finns olika tekniker för att parsa koden. Varje teknik behöver man lite tid för att förstå, låt säga att du använder och skriva en parser som shunting yard algoritm. Förstår du då shunting yard algoritmen så vet du om den är bättre eller sämre för att lösa ett problem. Om du tittar på 10 videos som inte tycker shunting yard är användbart så förstår du ändå eftersom du förstår tekniken och då förstår också om videos som beskriver den som dålig har rätt eller fel och varför de tycker shunting yard är olämplig. Det kan vara att de använt annan som pratt parser eller något annat.
Du vet om tekniken är bra eller dålig beroende på vad du vill lösa eftersom du förstår tekniken och hur den kan används. Om 100 andra säger att det är dåligt spelar det ingen roll för du förstår hur tekniken kan användas.

Om en utvecklare inte förstår hur exempelvis taggad data kan underlätta och göra säkrare kod, återanvändbar kod eller flexibel kod så kommer den inte förstå dess fördelar eller nackdelar för utvecklaren förstår inte tekniken.

För mig är tagged unions DOD, om man ser på DOD som en teknik för leka med minne för att lösa problem. Det kan vara att man bestämmer att DOD är något annat, då får jag vänta tills det dyker upp namn för denna typ av lösning

Generellt är annars att de som förstår hur man kan börja använda minnet för att lösa problem brukar inte ha några problem att förstå lösningar där det görs, inte i detalj men principen.

Använder man språk där det är förbjudet att leka med minnet kan det också bli ett hinder till att förstå eftersom man inte tränas i det

Permalänk
Medlem
Skrivet av Ingetledigtnamn:

Videon avfärdade helt och hållet uttalandet från @klk om att man som utvecklare blir mer effektiv om man använder DOD.

.

Fråga: Sitter lite dåligt till för att detaljgranska en video men kan du utveckla varför videon avfärdar det? För mig är det ett galet uttalande om man praktiserat tekniken.

En gissning är att DOD har olika betydelser för olika utvecklare och därför blir det missförstånd

Permalänk
Datavetare
Skrivet av klk:

Spelar hastighet någon roll för dig?
Har du objekt som är 32 byte får du in två stycken i en cache line, jämfört med som med 48 byte då det bara får plats en så har du snabbare kod. En cache line är på x86 64 byte för att ta ett exempel. 48 byte är en dum siffra för datorer

Klart hastigheten spelar roll, videon som @KAD postade är en riktigt bra förklaring av DOD (vad som beskrivs där är precis det jag uppfattat som målet med DOD). Men lite som videon du postade med Casey Muratori så tycker jag båda har en lite "dated" syn på vad en modern CPU faktiskt är kapabel till.

Cache är superkritiskt och dess viktighet ökar ju bara ju mer gapet mellan hur mycket en CPU-kärnan kan göra per tidsenhet och hur många instruktioner som går i drickat om man stallar på en RAM-access.

Men det har även HW-designerna fattat... Tar vi en 90-tals CPU var det relevant att minska mängden accesser som träffar mer än en cache-line, det var relevant att bara läsa data på sin rätta alignment etc. Men det finns domäner, är själv främst bekant med nätverk då jag skrivit flera TCP/IP-stackar varav en är designad för high-end/backbone routers, där man inte kan kontrollera exakt hur data hamnar alignmentmässigt.

Alla högpresterande CPUer jag känner till kan idag läsa/skriva till "fel" alignment utan att det kostar extra, i alla fall så länge det handlar om "vanliga" skalära operationer.

Också till skillnad från 90-tals CPUer är dagens high-end CPUer alla utrustade med "fetch-predictors" som "lär" sig accessmönstret i ditt program. Om det är lätt att förutsäga, det är direkt trivialt att förutsäga i fallet en array med structs som accessas sekventiellt, så kommer CPUn med nästan 100 % säkerhet kunna "gömma" accesslatens genom att se till att data redan finns i nästa cache-line där man kommer dit.

Vissa strategier inom DOD som historiskt var värdefull gör det faktiskt lite svårare för en modern CPU att gissa rätt. Men så länge det handlar om ett gäng arrayer som man rätt mycket accessar sekventiellt lär de ändå vara rätt 100 %-iga. Det blir problem när man har stora mängder "pointer-chasing", så länkade listor är inte din vän här...

Beskrivningen Andrew Kelly ger av DOD är som sagt exakt hur jag uppfattar tekniken. DOD är inte något man vill använda "precis överallt", det är något man använder för man måste och då är DOD ett bra sätt att göra något som är fundamentalt komplext till något som ändå är hanterbart.

Ser INTE hur tagged unions i sig är DOD. Tagged unions är ett verktyg man ofta kommer använda om man jobbar med DOD, men finns andra ställen där tagged-unions är värdefullt (sett flera nämna parsers och givet hur mycket jag använde typ-matching i C# i den parser jag just skrev hade det uppenbara varit att köra tagged-enums i t.ex. Rust).

Du nämner shunting yard. Där behövs en stack, men bara för att man ofta använder stackar applicerar man inte shunting yard. Stack är bara ett väldigt användbart verktyg där, precis som det är i många andra fall.

I.e. du verkar egentligen inte använda DOD, men du använder ofta tagged unions. Vilket lite ställer frågan: varför kör du med detta mönster

auto [success, error] = fooBar(...); if (success) { success case, but where is there resulting data??? Probably implicitly set somewhere } else { user "error" to figure what went wrong }

istället för att använda en sak tagged unions är riktigt bra till (ännu bättre i språk med native-stöd och pattern-matching av dem)

struct error { ...info about error... }; template<class T> using result = std::variant<T, error>; ... result<int> fooBar(...); ... auto fooBarResult = fooBar(...); if (auto number = std::get<int>(&fooBarResult)) { // success-case, do something with number } else { const error& e = std::get<error>(&fooBarResult); // error case, } // and/or pass around fooBarResult regardless of whether it was successful or not

Overhead här är en engångsgrej för varje projekt alt. något man lägger i ett eget bibliotek. I min mening lättare att läsa då det är uppenbart var returnerad data finns när det gick bra, och var felet lagras om det gick dåligt. Och det använder tagged unions för att kunna behandla båda fallen lika om/när man vill skicka vidare resultatet oavsett om det gick bra eller dåligt.

Har inget med DOD att göra, men är vettig användning av tagged unions (och normel för felhantering i Rust och delvis en pain-point i Go då den gör exakt detta fast utan native-stöd för tagged unions + pattern matching)

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Yoshman:

Alla högpresterande CPUer jag känner till kan idag läsa/skriva till "fel" alignment utan att det kostar extra, i alla fall så länge det handlar om "vanliga" skalära operationer.

För simd kod tar autovektorisering hand om alignment genom att slänga in en pre-loop så då behöver man inte bry sig så mycket men skriver man avx512 instruktioner direkt så påverkar det en del. Unaligned load/stores instruktionerna i sig har ingen extra kostnad men det finns en kostnad kopplat till split loads/stores när man skriver över två cachelines. På x86 är split stores något dyrare än split loads, man tappar runt 30%. Att ha både loads och stores aligned är bäst, om det inte är möjligt så tjänar man på att se till att iaf stores är aligned. Det finns även ett annat alignment problem på x86 som också kan vara intressant att känna till. När man läser tillbaka data som man precis skrivit så läser man direkt från store buffer (store-load forwarding). Den använder bara 12 bitar som tag vilket innebär att man får 4k byte aliasing, det går alltså långsammare om man ligger runt 4kb alignment mellan store och loads.

Inte för att det här är detaljer som de flesta utvecklare behöver bry sig om men det kan förklara varför man ibland plötsligt får högre prestanda i en benchmark.