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

Permalänk
Medlem
Skrivet av klk:

Ta följande exempel, skulle du kunna berätta hur kompilatorn/processorn gör om följande kod?
Vad tror du ligger bakom respektive design eller spelar det någon roll

#include <string> struct StructWithString { int int_val; double double_val; std::string string_val; }; struct StructWithBuffer { int int_val; double double_val; char buffer[32]; };

Det där är inte kod - som i instruktioner till datorn.
Det där deklarationer av ett par structar. De gör kompilatorn ingenting med - deklarationer i sig översätts ju inte av kompilatorn till någonting alls.

Permalänk
Medlem
Skrivet av klk:

Massor.

Om du lägger ner tid och optimerar en klass som klarar av samma jobb som kanske annars hade behövt 20 olika, då vinner du dels på att klassen är bättre anpassad för återanvändning, mindre buggar och investerad tid är inte bortkastad om det behövs ändringar och klassen klarar det.

Optimera kod tar tid och koden kan se lite lustig ut om man inte är van. Skrivs bara nya klasser med hårdkodad logik för just den uppgiften finns inte tid för optimeringar, även om den tiden fanns blir koden så komplex att det är omöjligt att hantera den.

1 optimerad klass är enklare än att optimera 20

Antalet klasser (om man nu envisas med objektorienterad programmering) skall inte vara baserat på hur man tror sig kunna optimera dem.
De klasser man har (och därmed antalet) skall baseras på vad som gör programstrukturen klar och redig och programmet begripligt.

De flesta optimeringar bör inte göras förrän efter att man konstaterat
a) Att optimeringar faktiskt behövs
b) Vad som tar tid, så man inte lägger tid på att optimera fel saker

Permalänk
Medlem
Skrivet av Erik_T:

Det där är inte kod - som i instruktioner till datorn.
Det där deklarationer av ett par structar. De gör kompilatorn ingenting med - deklarationer i sig översätts ju inte av kompilatorn till någonting alls.

Givetvis men min tanke var att det skulle vara självförklarande för man bygger logik kring data, koden som skrivs arbetar på data. Och datat avgör snabbheten. Så vad processorn måste göra är att bearbeta data annorlunda jämfört med vad som skrivits i koden om koden visat sig vara långsammare.

Då är de här två exemplen enklast möjliga för att beskriva om någon skulle vilja förklara det hela praktiskt. För det är ju gott om teoretiska experter i tråden

Permalänk
Medlem
Skrivet av Erik_T:

a) Att optimeringar faktiskt behövs
b) Vad som tar tid, så man inte lägger tid på att optimera fel saker

Går det att exemplifiera det, kan du ta något exempel på hur det fungerar rent praktiskt

Permalänk
Medlem
Skrivet av klk:

Givetvis men min tanke var att det skulle vara självförklarande för man bygger logik kring data, koden som skrivs arbetar på data. Och datat avgör snabbheten. Så vad processorn måste göra är att bearbeta data annorlunda jämfört med vad som skrivits i koden om koden visat sig vara långsammare.

Då är de här två exemplen enklast möjliga för att beskriva om någon skulle vilja förklara det hela praktiskt. För det är ju gott om teoretiska experter i tråden

Vad kompilatorer och processorer gör är ju baserat på den kod och data man matar in.
Det du presenterade var inte ens data - det var bara ett par deklarationer.

Utan kod så kan all data bara ignoreras av processorn. Är inget mer att göra eller förklara.

Permalänk
Medlem
Skrivet av Erik_T:

Vad kompilatorer och processorer gör är ju baserat på den kod och data man matar in.
Det du presenterade var inte ens data - det var bara ett par deklarationer.

Utan kod så kan all data bara ignoreras av processorn. Är inget mer att göra eller förklara.

När jag granskat genererad assemblerkod så är sådant här helt avgörande.

I exemplet visas två olika structar, de hanterar nästan samma data bara att en struct har en string medan den andra har en buffer för texten. självklart är där en string mer "användbart" och kräver mindre kod, och det är den lösning som i princip alltid används i GC kod.

Så varför då använda en buffer?
Andra structen som endast lagrar text internt och då har en maxgräns har fördelen att det bara är block, inga destruktorer eller konstruktorer körs och minnet ligger väl placerat.

Samma med den postade tabell klassen, ett enda stort minnesblock.Låt säga att du haft 1000 rader i en tabell och det ligger värden på varje rad som är objekt. radera 1000 rader och en hel hög kod måste köras för det som rensar upp.

Kod som refaktorerats med den klassen har sett betydande prestandaförbättringar i kombination med mindre domänspecifik kodmassa.

Så fort medlem i klass läggs till som hanterar sitt eget minnesblock blir klassen långsammare, har inte sett att processorer eller kompilatorer kan fixa till det

Permalänk
Medlem
Skrivet av klk:

Går det att exemplifiera det, kan du ta något exempel på hur det fungerar rent praktiskt

Nu pratar vi nybörjarnivå, men okej då.

Antag att vi har en funktion foo() som du tror skulle kunna göras snabbare om du lade ett par dagars jobb på det. Är det värt att göra?
Närmare undersökning visar att foo() tar 0.01 sekunder att köra, och körs endast en enda gång - nämligen under uppstarten av programmet.
Så är det värt att försöka minska tiden det tar för programmet att start med, i bästa fall, 0.01 sekunder. I de allra flesta fall är svaret nej.

Om man har obegränsat med tid så kan man ju försöka optimera varenda liten bit av den kod man skriver, men det har man vanligtvis inte.

Så man måste prioritera vad man använder sin tid till.

Man skall inte optimera saker som inte behöver optimeras - är koden redan tillräckligt snabb, och om den inte använder för mycket minne eller andra resurser, ja då behöver den inte optimeras utan man kan ägna sin tid åt viktigare saker.

Om koden behöver optimeras, då får man börja med att mäta och profilera för att ta reda på var i koden optimeringar skulle göra mest nytta.
Var i koden spenderar processorn mest tid? Om programmet använder för mycket minne, vad används minnet till? Och så vidare.

Vissa optimeringar kan man göra redan innan man skriver kod, nämligen vid val av algoritmer och datastrukturer. Man kan kanske inte göra ett optimalt val redan då, men man kan undvika många dåliga val.
Behöver man till exempel sortera en större mängd tal så är det nästan alltid bättre att använda quicksort än att använda bubblesort. Kan finnas bättre val, men vi vet att bubblesort är en av de sämre sorteringsalgoritmerna, och att quicksort är en relativt bra algoritm.
Har man data som man ofta behöver söka i, så är det lätt att inse att en länkad lista är ett dåligt val för hur man lagrar datat, så att man bör välja en lämpligare datastruktur.
Och så vidare.

Allt det här är sånt som du redan borde kunna. Det är sånt som alla som programmerar yrkesmässigt bör känna till.

Permalänk
Medlem
Skrivet av Erik_T:

Allt det här är sånt som du redan borde kunna. Det är sånt som alla som programmerar yrkesmässigt bör känna till.

Och det gör jag så kan du dra versionen för när du faktiskt börjar optimera, hur gör du då?

Beskriv hur du skriver om kod för att den skall gå snabbare

Skrivet av Erik_T:

Vissa optimeringar kan man göra redan innan man skriver kod, nämligen vid val av algoritmer och datastrukturer. Man kan kanske inte göra ett optimalt val redan då, men man kan undvika många dåliga val.
Behöver man till exempel sortera en större mängd tal så är det nästan alltid bättre att använda quicksort än att använda bubblesort. Kan finnas bättre val, men vi vet att bubblesort är en av de sämre sorteringsalgoritmerna, och att quicksort är en relativt bra algoritm.
Har man data som man ofta behöver söka i, så är det lätt att inse att en länkad lista är ett dåligt val för hur man lagrar datat, så att man bör välja en lämpligare datastruktur.
Och så vidare.

Här väljer du färdig kod. Olika typer kod som följer med.
Jag menar när du skriver koden

Permalänk
Medlem
Skrivet av klk:

När jag granskat genererad assemblerkod så är sådant här helt avgörande.

I exemplet visas två olika structar, de hanterar nästan samma data bara att en struct har en string medan den andra har en buffer för texten. självklart är där en string mer "användbart" och kräver mindre kod, och det är den lösning som i princip alltid används i GC kod.

Så varför då använda en buffer?
Andra structen som endast lagrar text internt och då har en maxgräns har fördelen att det bara är block, inga destruktorer eller konstruktorer körs och minnet ligger väl placerat.

Samma med den postade tabell klassen, ett enda stort minnesblock.Låt säga att du haft 1000 rader i en tabell och det ligger värden på varje rad som är objekt. radera 1000 rader och en hel hög kod måste köras för det som rensar upp.

Kod som refaktorerats med den klassen har sett betydande prestandaförbättringar i kombination med mindre domänspecifik kodmassa.

Så fort medlem i klass läggs till som hanterar sitt eget minnesblock blir klassen långsammare, har inte sett att processorer eller kompilatorer kan fixa till det

Nu pratar du om hur structarna fråga används - vilket inte går att avgöra bara genom att titta på structarna själva.

Det är stor skillnad på om en klass instantieras bara ett par gånger under programmets livstid eller om det görs tiotusentals eller fler gånger.
I det förra fallet så spelar är exekveringstiden för konstruktor/destruktor oftast irrelevant. I det senare fallet kan det spela stor roll - eller inte göra någon märkbar skillnad.

Permalänk
Medlem
Skrivet av klk:

Och det gör jag så kan du dra versionen för när du faktiskt börjar optimera, hur gör du då?

Beskriv hur du skriver om kod för att den skall gå snabbare

Det beror ju på hur koden ser ut till att börja med! Så det kan jag inte beskriva i generella termer.
Att ta fram ett icke-trivialt exempel som demonstration skulle ta mycket mer tid och energi än jag är villig att spendera på dig.

Permalänk
Medlem
Skrivet av Erik_T:

Det beror ju på hur koden ser ut till att börja med! Så det kan jag inte beskriva i generella termer.
Att ta fram ett icke-trivialt exempel som demonstration skulle ta mycket mer tid och energi än jag är villig att spendera på dig.

Men om du ser mina två structar och vet skillnaden så är det ett vanligt exempel på hur kod optimeras.
Sådant här bör RUST utvecklare kunna i och med att det där finns så mycket regler och de då blir vana vid hur minnet behöver hanteras.

Annat som att försöka anpassa klassers storlek efter cache lines (64 byte) eller kanske använda flaggor i ett större tal istället för enskilda medlemmar som bara håller state

Skall man köra flera trådar och dra nytta av deras extra hastighet utan att tänka på minimalt med synkronisering så bli inte förvånad om det knappt blir någon förbättring o.s.v

Permalänk
Medlem
Skrivet av Erik_T:

I det förra fallet så spelar är exekveringstiden för konstruktor/destruktor oftast irrelevant. I det senare fallet kan det spela stor roll - eller inte göra någon märkbar skillnad.

Men den typen av kod behöver troligen inte optimeras alls, just nu pratar vi väl kod som drar nytta av mer hastighet.

Annan kod, där kan du lika gärna använda python ;), 1000 gånger långsammare men vem bryr sig om det tar en 0.001 sekund eller en sekund