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

Permalänk
Medlem
Skrivet av Yoshman:

Du har redan postat det projektet. Hade inte hört talas om det innan, men när jag sökte på det visade det sig att endast de system som hanterade front-end var utvecklade i Java. Majoriteten av systemet var i C++ med vissa kritiska delar i Erlang.

Stämmer inte, jag tvivlar på att de berättar detaljer utåt.
Det kan vara så att de försökte "rädda" lösningen med C++ när de upptäckte att det inte fungerade med java för det tog några år innan de konkade. Hade detaljerad kunskap om vad som pågick och var med och försöka påverka ledningen, tror det var 1999-2000.
Men det var mycket prestige och pengar satsade så att berätta "det här kommer hända" var inte lätt att få igenom.

Permalänk
Medlem
Skrivet av klk:

När jag började programmera som yrke så var det VB de flesta satt i. För varje C++ programmerare i windows gick det säkert minst 10 VB programmerare. Helt omöjligt för en C++ utvecklare att ha någon form av konstruktiv diskussion med VB utvecklare, två helt olika världar.

Skrivet av klk:

Du menar försöker koda C++. Kan du C++ gör du inget annat än prototyper och liknande med enklare språk.

Finns jättemånga som försöker koda C++ men borde fokusera på något annat.

Livet är hårt Ibland måste man träna för att bli bra. Människor som inte klarar av kritik behöver växa upp.

Skrivet av klk:

"Vi" ? Din diskussion förstår jag inte (som jag tidigare nämnt för dig)

- Du slängde ur dig ett argument som jag uppfattade som nedlåtande om VB-utvecklare.
- Jag sa: "Du behöver inte uttrycka dig nedlåtande om folk baserat på deras preferenser gällande programmeringsspråk"
- Du svarar med: "Livet är hårt Ibland måste man träna för att bli bra. Människor som inte klarar av kritik behöver växa upp."

... så vem ska träna mer och kan du slutföra resonemanget innan du hoppar vidare till din sons gymnasielärare eller andra sidospår?!

Permalänk
Datavetare
Skrivet av klk:

Stämmer inte, jag tvivlar på att de berättar detaljer utåt.
Det kan vara så att de försökte "rädda" lösningen med C++ när de upptäckte att det inte fungerade med java för det tog några år innan de konkade. Hade detaljerad kunskap om vad som pågick och var med och försöka påverka ledningen, tror det var 1999-2000.
Men det var mycket prestige och pengar satsade så att berätta "det här kommer hända" var inte lätt att få igenom.

Nu är detta (nog) spekulation med minder än att folk med insyn kan säga om det är rätt eller fel. Men vid sökning fick jag denna sammanfattning av projektet

Citat:

EHPT developed telecom management systems, billing, and network solutions, leveraging several key technologies:
1. Erlang - Used for telecom switching and fault-tolerant systems (inspired by Ericsson).
2. C and C++ - Core system programming for performance-critical applications.
3. Java - Used in later years for network management and service applications.
4. SQL & PL/SQL - Database management for billing and subscriber records.
5. HP-UX and Solaris - Unix-based OSes commonly used in telecom systems.
6. CORBA & RMI - Middleware communication protocols.

Men kvittar egentligen. Statistiken över misslyckade projekt är ingen kul läsning, det rätt mycket oavsett teknikval.

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:

Nu är detta (nog) spekulation med minder än att folk med insyn kan säga om det är rätt eller fel.

Så är det

Permalänk
Skrivet av klk:

Förutom att det tar tid att kontrollera access mot minne så är det en hel massa optimeringar en kompilator inte kan göra.

Nu måste jag syna dina uttalanden igen.

Kan du ge exempel på denna "hel massa" optimeringar som inte kan göras i ett språk med GC? Detta handlar inte om att jag vill lära mig mer om detta, så hänvisa mig inte till ChatGPT. Jag vill inte ha anekdotisk bevisföring, förenklande förklaringar eller utvikningar om annat, bara konkreta exempel på vilka optimeringar som inte går att göra då språket har GC. Använd gärna vedertagen terminologi. Jag har yrkesmässigt skrivit kompilatoroptimeringar för C och C++ i mer än 20 år, så jag skall nog kunna hänga med även om det skulle bli lite tekniskt...

Permalänk
Medlem
Skrivet av Ingetledigtnamn:

Nu måste jag syna dina uttalanden igen.

Kan du ge exempel på denna "hel massa" optimeringar som inte kan göras i ett språk med GC? Detta handlar inte om att jag vill lära mig mer om detta, så hänvisa mig inte till ChatGPT. Jag vill inte ha anekdotisk bevisföring, förenklande förklaringar eller utvikningar om annat, bara konkreta exempel på vilka optimeringar som inte går att göra då språket har GC. Använd gärna vedertagen terminologi. Jag har yrkesmässigt skrivit kompilatoroptimeringar för C och C++ i mer än 20 år, så jag skall nog kunna hänga med även om det skulle bli lite tekniskt...

börjar med std::vector och då jämför med rusts vector som kallas "vec"
Om du skall hämta minne säkert behöver man kontrollera.
C++

std::vector<int> numbers = {10, 20, 30, 40, 50}; std::cout << "First element: " << numbers[0] << "\n"; // 10 std::cout << "First element: " << numbers[999] << "\n"; // PANG (Undefined Behavior)

Rust

let mut numbers = vec![10, 20, 30, 40, 50]; println!("First element: {}", numbers[0]); // 10 println!("First element: {}", numbers[999]); // panic (gissar att det liknar exceptions i c++)

Här har du exempelkod när kompilatorn optimerar med simd
https://godbolt.org/z/a7dovoP3s

Permalänk
Datavetare
Skrivet av klk:

börjar med std::vector och då jämför med rusts vector som kallas "vec"
Om du skall hämta minne säkert behöver man kontrollera.
C++

std::vector<int> numbers = {10, 20, 30, 40, 50}; std::cout << "First element: " << numbers[0] << "\n"; // 10 std::cout << "First element: " << numbers[999] << "\n"; // PANG (Undefined Behavior)

Rust

let mut numbers = vec![10, 20, 30, 40, 50]; println!("First element: {}", numbers[0]); // 10 println!("First element: {}", numbers[999]); // panic (gissar att det liknar exceptions i c++)

Här har du exempelkod när kompilatorn optimerar med simd
https://godbolt.org/z/a7dovoP3s

Just a FYI: Rust använder inte GC och motsvarande Rust program för det du länkar på compiler explorer kan skrivas t.ex. så här

#[no_mangle] pub fn add(a: Vec<f32>, b: Vec<f32>) -> Vec<f32> { a.iter().zip(b.iter()).map(|(x, y)| x + y).collect() } pub fn main() { let a = vec![1f32; 1024]; let b = vec![2f32; 1024]; let r = add(a, b); println!("{:?}", &r[..10]); }

https://godbolt.org/z/xEzo8bvPf

där additionen använder SIMD på rätt exakt samma sätt som C++ versionen (finns std::simd stöd i Rust som ger betydligt bättre stöd, även om det faller kort av att direkt köra intrinsics då det ska fungera på alla CPUer).

Men inget av ovan var svar på @Ingetledigtnamn högst intressanta fråga!

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 klk:

börjar med std::vector och då jämför med rusts vector som kallas "vec"
Om du skall hämta minne säkert behöver man kontrollera.
C++

std::vector<int> numbers = {10, 20, 30, 40, 50}; std::cout << "First element: " << numbers[0] << "\n"; // 10 std::cout << "First element: " << numbers[999] << "\n"; // PANG (Undefined Behavior)

Rust

let mut numbers = vec![10, 20, 30, 40, 50]; println!("First element: {}", numbers[0]); // 10 println!("First element: {}", numbers[999]); // panic (gissar att det liknar exceptions i c++)

Här har du exempelkod när kompilatorn optimerar med simd
https://godbolt.org/z/a7dovoP3s

Vilket bara säger att det kan behövas extra instruktioner för att upptäcka out-of-bounds indexering, men säger absolut ingenting om vilka optimeringar som kompilatorn kan eller inte kan göra.
Med andra ord, det besvarar inte frågan.

Permalänk
Medlem
Skrivet av Erik_T:

Vilket bara säger att det kan behövas extra instruktioner för att upptäcka out-of-bounds indexering, men säger absolut ingenting om vilka optimeringar som kompilatorn kan eller inte kan göra.
Med andra ord, det besvarar inte frågan.

Tittade du på kodexemplet (länken). Jag kan inte läsa dina tankar så då får du förtydliga vad du faktiskt vill veta eller kanske offra dig lite och göra något exempel själv och se om det går att förbättra i C++.

Permalänk
Medlem
Skrivet av Yoshman:

där additionen använder SIMD på rätt exakt samma sätt som C++ versionen (finns std::simd stöd i Rust som ger betydligt bättre stöd, även om det faller kort av att direkt köra intrinsics då det ska fungera på alla CPUer).

Jag tänkte faktiskt lägga till att "snart kommer Yoshman" och visar att samma går i rust. Och det är korrekt, så länge du kan visa kompilatorn vad som är i minnet kan den optimera koden. Problemet uppstår när du inte kan visa kompilatorn vad minnet innehåller. Då kan den inte optimera
Det är då du börjar skriva massa konstig kod i C/C++ som är så osäker

Permalänk
Medlem

Jag skulle vara förvånad om C och C++ skulle tappa någon större betydelse den närmaste tiden men jag är ju inte så intresserad av nya språk och paradigm, så jag är väl fel person att fråga.

Visa signatur

5820k 4,2 GHz @ 1,220 V
6950 XT undervolt

[FAQ: Formatera text i inläggen med BB-kod]
#17675721

Permalänk
Medlem
Skrivet av klk:

Tittade du på kodexemplet (länken). Jag kan inte läsa dina tankar så då får du förtydliga vad du faktiskt vill veta eller kanske offra dig lite och göra något exempel själv och se om det går att förbättra i C++.

Frågan, som du citerade i ditt svar, gällde GC och du svarade med out-of-bounds-checkar och jämförde med ett språk utan GC. Du behöver inte läsa tankar, det räcker att du läser frågan.

Skrivet av Ingetledigtnamn:

Kan du ge exempel på denna "hel massa" optimeringar som inte kan göras i ett språk med GC?

Permalänk
Medlem
Skrivet av KAD:

Frågan, som du citerade i ditt svar, gällde GC och du svarade med out-of-bounds-checkar och jämförde med ett språk utan GC. Du behöver inte läsa tankar, det räcker att du läser frågan.

Kan tänka mig att alla optimeringar C++ kan göra som relaterar till buffrar där kompilatorn inte vet om vad som finns i buffern, de fungerar inte att göra i C#

En gissning är att C# heller inte kan placera ett objekt på utpekad minnesadress.

Känner inte till hur duktig C# är på att använda stacken och inte lägga det på heapen men där tror jag också det finns en del brister i förhållande på hur man kan laborera i C++

Permalänk
Medlem
Skrivet av KAD:

Frågan, som du citerade i ditt svar, gällde GC och du svarade med out-of-bounds-checkar och jämförde med ett språk utan GC. Du behöver inte läsa tankar, det räcker att du läser frågan.

Lägger till en till som inte finns in C++ än men som är på gång. Reflection i C++ som kommer i C++26 skall enligt de som vet vara c.a. 4 gånger snabbare än reflection i C#

Permalänk
Datavetare
Skrivet av klk:

Kan tänka mig att alla optimeringar C++ kan göra som relaterar till buffrar där kompilatorn inte vet om vad som finns i buffern, de fungerar inte att göra i C#

En gissning är att C# heller inte kan placera ett objekt på utpekad minnesadress.

Antar att du menar "C# kan inte läsa/skriva till t.ex. minnesmappad I/O med en fix adress". För annars får du förklara hur C++ kan lägga objekt på en specifik address...

Långsammare men säkrare metod som inte kräver unsafe

using System.Runtime.InteropServices; class Program { static void Main() { IntPtr ptr = new IntPtr(0xa000); // Could work if we are running 16-bit real-mode.... int value = Marshal.ReadInt32(ptr); Console.WriteLine($"Value at address {ptr}: {value}"); } }

C/C++ style raw-pointers

using System; class Program { static unsafe void Main() { int* ptr = (int*)0xa000; Console.WriteLine($"Pointer address: {(ulong)ptr:X}"); Console.WriteLine($"Pointer content: {*ptr:X}"); } }

Skrivet av klk:

Känner inte till hur duktig C# är på att använda stacken och inte lägga det på heapen men där tror jag också det finns en del brister i förhållande på hur man kan laborera i C++

C# särskiljer explicit på "value types" som den lägger på stacken alt. bäddar in i ägande type, samt "reference types" som ligger på heap:en.

Så det går.

Vad man inte kan göra i C# (men väl i Go som också har Gc) är att skapa en array av "value objects" för att sedan returnera enskilt element genom att ta adressen. Value objects kopieras i C# när man gör get/set, medan referens types hanteras rätt mycket som pekare i C++.

Det skrivet: via P/Invoke och unsafe kan man rätt mycket göra "vad som helst", t.ex. använda Win32 APIet och DirectX direkt från C#. Så: "try harder, this was not the GC-flaws you where looking for".

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:

Det skrivet: via P/Invoke och unsafe kan man rätt mycket göra "vad som helst", t.ex. använda Win32 APIet och DirectX direkt från C#. Så: "try harder, this was not the GC-flaws you where looking for".

Ja det vet jag. Använt det själv även om det var många år sedan (P/invoke) men då har du inte säkerheten. Språket kan inte "glänsa" längre
Och så kodar inte C# utvecklare, mycket ovanligt att de ens funderar över hur minnet sköts

Permalänk
Medlem
Skrivet av klk:

Tittade du på kodexemplet (länken). Jag kan inte läsa dina tankar så då får du förtydliga vad du faktiskt vill veta eller kanske offra dig lite och göra något exempel själv och se om det går att förbättra i C++.

Kodexemplet har inte ett dugg med frågan att göra. Återigen så bara virrar du iväg på ett sidospår istället för att svara på frågor.
Frågan du (inte) besvarade gällde vilka optimeringar som inte går att göra i ett språk med GC - eftersom du hade hävdat att det finns en massa sådana.
Indexering av vektorer är irrelevant för frågan.

Permalänk
Medlem
Skrivet av Erik_T:

Kodexemplet har inte ett dugg med frågan att göra. Återigen så bara virrar du iväg på ett sidospår istället för att svara på frågor.
Frågan du (inte) besvarade gällde vilka optimeringar som inte går att göra i ett språk med GC - eftersom du hade hävdat att det finns en massa sådana.
Indexering av vektorer är irrelevant för frågan.

Jag tycker du skall gå in på något forum eller liknande som har C++ diskussioner. Där tycker jag du skall berätta att C# kan göra allt som C++ kan göra. Försök sedan ta diskussionen...

Ingen kommer hålla med mig här av de aktiva. Programmering är hyperkänsligt för många och det handlar mycket om känslor.
Det spelar ingen roll vad jag säger till dig, du kommer aldrig hålla med mig

Men visst är det märkligt att det inte ploppar upp fler applikationer skrivna i C# där det är krav på prestanda

Permalänk
Medlem
Skrivet av klk:

Jag tycker du skall gå in på något forum eller liknande som har C++ diskussioner. Där tycker jag du skall berätta att C# kan göra allt som C++ kan göra. Försök sedan ta diskussionen...

Ingen kommer hålla med mig här av de aktiva. Programmering är hyperkänsligt för många och det handlar mycket om känslor.
Det spelar ingen roll vad jag säger till dig, du kommer aldrig hålla med mig

Men visst är det märkligt att det inte ploppar upp fler applikationer skrivna i C# där det är krav på prestanda

Som sagt var - återigen så virrar du ut på ett irrelevant sidospår utan att svara på frågan.
Jag har inte sagt ett enda ord om vad C# kan eller inte kan göra.

Permalänk
Medlem
Skrivet av klk:

Jag tycker du skall gå in på något forum eller liknande som har C++ diskussioner. Där tycker jag du skall berätta att C# kan göra allt som C++ kan göra. Försök sedan ta diskussionen...

Varför ska @Erik_T gå in på ett separat forum och skapa förvirring för att du gör märkliga påstående här? Trasig logik ... svara på frågan istället.

Permalänk
Medlem
Skrivet av klk:

Ingen kommer hålla med mig här av de aktiva. Programmering är hyperkänsligt för många och det handlar mycket om känslor.
Det spelar ingen roll vad jag säger till dig, du kommer aldrig hålla med mig

Om du nu är så jäkla tuff och gillar hårda klimat så slut med sådant här gnäll. Baserat på:

Skrivet av klk:

Livet är hårt Ibland måste man träna för att bli bra. Människor som inte klarar av kritik behöver växa upp.

Jag kan inte svara för alla andra men jag håller inte med dig eftersom det du säger är inkorrekt och/eller svammel. Om du vill ändra min åsikt så börja med att bevisa det du säger!

Permalänk
Skrivet av klk:

börjar med std::vector och då jämför med rusts vector som kallas "vec"
Om du skall hämta minne säkert behöver man kontrollera.
C++

std::vector<int> numbers = {10, 20, 30, 40, 50}; std::cout << "First element: " << numbers[0] << "\n"; // 10 std::cout << "First element: " << numbers[999] << "\n"; // PANG (Undefined Behavior)

Rust

let mut numbers = vec![10, 20, 30, 40, 50]; println!("First element: {}", numbers[0]); // 10 println!("First element: {}", numbers[999]); // panic (gissar att det liknar exceptions i c++)

Här har du exempelkod när kompilatorn optimerar med simd
https://godbolt.org/z/a7dovoP3s

Nu vet jag inte riktigt vad detta inlägg har med optimeringar att göra. Det är inte en kompilatoroptimering att C++ inte gör range checks. Och, som andra tidigare har påpekat, så har inte Rust GC så ditt svar är inte ett svar på min fråga. Men jag nappar på detta för att lära dig lite om vad kompilatorn, denna mytiska best, faktiskt kan göra.

Om vi kikar på X86-koden som genereras för Rust-koden @Yoshman postade så är det inga problem att lägga ut AVX-kod även i ett språk med range checks. (Rad 120)

Men hur kan kompilatorn låta bli att göra range check här? Jo, optimeringar

Vi kör C++ för enkelheten skull, där vi båda två känner oss mer hemma. Din funktion innehåller inte tillräckligt med information för att man skall kunna göra en range check, så låt oss byta ut dina råa pekare till vektor-referenser:

void add(const std::vector<float> &p1_, const std::vector<float> &p2_, std::vector<float> &pResult_, size_t uSize) { for(size_t u = 0; u < uSize; u++) { pResult_[u] = p1_[u] + p2_[u]; } }

Sedan utökar vi C++ till att även göra range checks. Det borde ge kod liknande denna om man inline-expanderat range checken. Lite mer precis info borde skickas till panic-anropet, men det är inte den intressanta delen här.

void add(const std::vector<float> &p1_, const std::vector<float> &p2_, std::vector<float> &pResult_, size_t uSize) { for(size_t u = 0; u < uSize; u++) { if (u >= p1_.size_) panic(); if (u >= p2_.size_) panic(); if (u >= pResult_.size_) panic(); pResult_[u] = p1_[u] + p2_[u]; } }

Det krävs inte särskilt avancerad analys för att se att index-variablen u kommer gå från 0 till uSize. Om range check vore ett vanligt mönster i språket skulle det finnas optimeringar som letade efter just detta: loop med känd trip count och range checks i loop kroppen. Här kan kompilatorn välja att lägga ut två loopkroppar och välja vilken som körs baserat på loopens slutvärde:

void add(const std::vector<float> &p1_, const std::vector<float> &p2_, std::vector<float> &pResult_, size_t uSize) { if (uSize >= p1_.size_ || uSize >= p2_.size_ || uSize >= pResult_.size_) { for(size_t u = 0; u < uSize; u++) { if (u >= p1_.size_) panic(); if (u >= p2_.size_) panic(); if (u >= pResult_.size_) panic(); pResult_[u] = p1_[u] + p2_[u]; } } else { for(size_t u = 0; u < uSize; u++) { if (u >= p1_.size_) panic(); if (u >= p2_.size_) panic(); if (u >= pResult_.size_) panic(); pResult_[u] = p1_[u] + p2_[u]; } } }

Om uSize skulle vara ett ogiltigt index i någon av arrayerna kommer den inledande testen vara sann och man kommer köra den första loopen. I den andra loopen vet kompilatorn att uSize inte kommer trigga någon av range checkerna och den kan göra exakt samma optimeringar den skulle kunna göra på motsvarande C++-kod. Exempelvis använda SIMD-instruktioner.

Du kan argumentera att det läggs ut kod i onödan, men GCC gör liknande tricks för att kunna lägga ut SIMD-kod. Om du tittar på koden i din Godbolt-länk så inleds funktionen add med ett antal tester som säkerställer att pResult_ inte överlappar med p1_ och p2_. Utan dessa tester skulle inte GCC kunna lägga ut AVX-instructioner då det finns risk för aliasing mellan dina råa pekare.

Om vi går ett steg till och blandar in function inlining så blir det ännu bättre kod. I lekmannens ögon handlar inliningen om att bli av med overheaden förknippad med att göra ett funktionsanrop. Man blir av med overheaden, men det är långt viktigare att kompilatorn kan göra optimeringar över funktionsanropet. Efter inlining har man fått en lokal kopia av funktionskroppen som kan skräddarsys för de parametrar som skickades i just det här anropet.

Om både vektor-konstruktorerna och add inline-expanderas i main vet kompilatorn att vector1, vector2, och result har storleken 1024. På samma sett vet kompilatorn att p1_, p2_ och pResult_ i adds kropp refererar till vector1, vector2 och result (alla med storleken 1024) och att uSize kommer ha värdet 1024. Ingen rangetesterna kommer lösa ut och den kan plocka bort både testerna och loop-kroppen med testerna i. En trip count på 1024 är nice power of 2 så man kan rulla upp loopen precis så mycket man vill utan att behöva lägga ut en extra loop för att utföra de "rest"-iterationer som den upprullade loopen inte hanterade.

Bara för att språket upprätthåller en viss nivå av minnessäkerhet så behöver inte det betyda att den genererade koden blir långsam. Med kompilatoroptimeringar blir den kritiska koden ofta precis lika effektiv som den du skriver i C++. Det är dags att göra dig av med dina fördomar om andra språk och programmerare som använder dem.

Permalänk
Medlem
Skrivet av Ingetledigtnamn:

Nu vet jag inte riktigt vad detta inlägg har med optimeringar att göra. Det är inte en kompilatoroptimering att C++ inte gör range checks.

Poängen är att om kompilatorn inte behöver göra kontroller så är den friare att göra om koden. Speciellt viktigt blir detta när kompilatorn inte vet om vad som finns i minnet.
I den kod du visar så skriver du så kompilatorn hela tiden vet vad som finns där, hur gör du när kompilatorn inte vet det?

Tipsar om videon jag postade tidigare i tråden och som är en vanlig teknik som används i C och C++ kod (mer i C). Istället för att ha många små allokeringar skrivs kod så mycket kan läggas i ett enda block, kapslar in det och skriver egen kod för att sköta logiken. Troligen den mest vanliga tekniken för att få upp hastighet.

Här är ett exempel på "minneskarta" från en lösning som kan läsa databas data (SELECT frågor) till minnet och används som ett DTO objekt. Eftersom man kan fråga databasen om information som databasen levererar från SELECT frågan kan du räkna ut hur mycket som behövs för att hantera resultatet i minnet.

En enda allokering för datat

*Sample data layout* ╔═══════╦════════════╦═════════════════════════╦════╗ ║ int32 ║ int64 ║ string ║int8║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══════╩════════════╬═════════════════════════╩════╝ ║ meta data for each ║ ║ row ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚════════════════════╝

Permalänk
Medlem
Skrivet av klk:

Poängen är att om kompilatorn inte behöver göra kontroller så är den friare att göra om koden. Speciellt viktigt blir detta när kompilatorn inte vet om vad som finns i minnet.
I den kod du visar så skriver du så kompilatorn hela tiden vet vad som finns där, hur gör du när kompilatorn inte vet det?

Behöver en range-check göras under runtime så krävs det naturligtvis ett par extra instruktioner. Men de hindrar inte optimeringsfasen i kompilatorn från att göra sitt jobb, och med en modern CPU så kommer en sådan range-check antagligen inte slöa ner koden märkbart, eller knappt ens mätbart.

Citat:

Tipsar om videon jag postade tidigare i tråden och som är en vanlig teknik som används i C och C++ kod (mer i C). Istället för att ha många små allokeringar skrivs kod så mycket kan läggas i ett enda block, kapslar in det och skriver egen kod för att sköta logiken. Troligen den mest vanliga tekniken för att få upp hastighet.

Här är ett exempel på "minneskarta" från en lösning som kan läsa databas data (SELECT frågor) till minnet och används som ett DTO objekt. Eftersom man kan fråga databasen om information som databasen levererar från SELECT frågan kan du räkna ut hur mycket som behövs för att hantera resultatet i minnet.

En enda allokering för datat

*Sample data layout* ╔═══════╦════════════╦═════════════════════════╦════╗ ║ int32 ║ int64 ║ string ║int8║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══════╩════════════╬═════════════════════════╩════╝ ║ meta data for each ║ ║ row ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚════════════════════╝

Jaha? Den tekniken borde ju gå fint att använda i de flesta programmeringsspråk. Med eller utan manuell minneshantering.
Så vad är din poäng? Alltså förutom att svamla iväg på ytterligare ett irrelevant sidospår?

Permalänk
Datavetare
Skrivet av klk:

Poängen är att om kompilatorn inte behöver göra kontroller så är den friare att göra om koden. Speciellt viktigt blir detta när kompilatorn inte vet om vad som finns i minnet.
I den kod du visar så skriver du så kompilatorn hela tiden vet vad som finns där, hur gör du när kompilatorn inte vet det?

Tipsar om videon jag postade tidigare i tråden och som är en vanlig teknik som används i C och C++ kod (mer i C). Istället för att ha många små allokeringar skrivs kod så mycket kan läggas i ett enda block, kapslar in det och skriver egen kod för att sköta logiken. Troligen den mest vanliga tekniken för att få upp hastighet.

Här är ett exempel på "minneskarta" från en lösning som kan läsa databas data (SELECT frågor) till minnet och används som ett DTO objekt. Eftersom man kan fråga databasen om information som databasen levererar från SELECT frågan kan du räkna ut hur mycket som behövs för att hantera resultatet i minnet.

En enda allokering för datat

*Sample data layout* ╔═══════╦════════════╦═════════════════════════╦════╗ ║ int32 ║ int64 ║ string ║int8║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══════╩════════════╬═════════════════════════╩════╝ ║ meta data for each ║ ║ row ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚════════════════════╝

C++ har ganska strikta regler kring minne (Go har på vissa sätt fler frihetsgrader för kompilatorn att hantera minne då minnesmodellen är mindre strikt än C++).

Och det de flesta kanske känner till är C++ (och C som sedan C11 i.o.f.s. har "restrict" för att kunna lyfta detta) har rätt strikta alias-regler.

Dessa två funktioner gör logiskt rätt mycket samma sak

C++

int fsck_alias_rules(const int *a, int *b) { *b += *a; return *a; }

detta ger detta med gcc/-O3

fsck_alias_rules(int const*, int*): mov eax, DWORD PTR [rdi] add DWORD PTR [rsi], eax mov eax, DWORD PTR [rdi] ret

Rust (pekare i Go fungerar på samma sätt som Rust m.a.p just detta)

pub fn sane_alias_rules(a: &i32, b: &mut i32) -> i32 { *b += *a; return *a; }

vilket ger det mer väntade

sane_alias_rules: mov eax, dword ptr [rdi] add dword ptr [rsi], eax ret

I.e. det är två exempel där C++-kompilatorn har mindre möjligheter till optimeringar jämfört med modernare språk.

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 Erik_T:

Behöver en range-check göras under runtime så krävs det naturligtvis ett par extra instruktioner. Men de hindrar inte optimeringsfasen i kompilatorn från att göra sitt jobb, och med en modern CPU så kommer en sådan range-check antagligen inte slöa ner koden märkbart, eller knappt ens mätbart.

När du behöver prestanda och anstränger dig så för att få upp prestanda, då är det oftast väldigt viktigt.

Ber om ursäkt om det är repetering men har sagt tidigare i tråden.
C eller C++ väljs för att man inte har något annat alternativ. Det kan vara svårt att svälja om man älskar annat språk. Jag tror inte de som använder C eller C++ älskar språken utan de används för att de är bäst för det som skall göras.

Bara det att om C++ används så har man en hel hög med kompilatorer att välja på jämfört med en eller kanske två för andra språk.

Och det är svårt och diskutera lösningar mellan de som har sitt favoritspråk och de som måste lösa problem och välja verktyg efter det.

Permalänk
Medlem
Skrivet av Yoshman:

I.e. det är två exempel där C++-kompilatorn har mindre möjligheter till optimeringar jämfört med modernare språk.

Så varför väljs inte GO till spelmotorer och annat som behöver prestanda? Det är ju både supersnabbt och enkelt

Permalänk
Datavetare
Skrivet av klk:

Så varför väljs inte GO till spelmotorer och annat som behöver prestanda? Det är ju både supersnabbt och enkelt

Kollar man anledningen till varför Microsoft valde Go över andra språk till Typescript-kompilatorn är det rätt mycket en önskelista över saker man också vill ha i spel.

Men tyvärr finns en väldigt specifik teknisk anledning varför Go i nuläget är ett dåligt val för krävande spel. Alla relevanta grafik-APIer (DirectX, Vulkan och Metal) har likt de flesta bibliotek ett C-baserat API (specifikt så är ABI).

En av Go:s killer-features är hur man optimerat hela runtime för att effektivt kunna köra tiotusentals samtida Go-rutiner. En förutsättning för att det överhuvudtaget ska fungera är att man upp till punkten där man gör systemanrop till OS-kärnan använder kod som också är byggd med Go så den innehåller rätt runtime-stöd (primärt kunna växa call-stack:en vid behov).

Rent praktiskt är det superenkelt att anropa C-bibliotek från Go-kod då miljön har stöd för att parsa C-header-filer och göra nödvändig FFI-kod. Tyvärr måste man säkerställa att man har en call-stack uppsatt som C/C++/Rust etc förväntas sig innan ett sådant anrop, vilket gör att det är betydligt högre overhead att anropa C-kod från Go än från t.ex. C# (C# har explicit designas för noll overhead att anropa C-kod, bl.a. då man vill ha effektivt stöd för att använda Win32, DirectX etc).

Så innan någon skriver en Go-native implementation av DirectX, Vulkan och/eller Metal är tyvärr just spel ett område där Go passar illa. Vilket är trist givet att det bortsett från just den detaljen annars nog är den bästa tänkbara miljön för just det use-case:et.

Edit: det skrivet, Go har p.g.a. sin egenskaper en ökande popularitet som spel-server då man inte har problematiken med grafik-biblioteken där, samtidigt som man får fördelarna med lysande I/O-prestanda och skalning.

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:

Så innan någon skriver en Go-native implementation av DirectX, Vulkan och/eller Metal är tyvärr just spel ett område där Go passar illa. Vilket är trist givet att det bortsett från just den detaljen annars nog är den bästa tänkbara miljön för just det use-case:et.

Finns massor av annan funktionalitet som också behöver snabbhet. Kan vi räkna med att det dyker upp snabba GO lösningar där?

Ta allt inom AI

Permalänk
Datavetare
Skrivet av klk:

Finns massor av annan funktionalitet som också behöver snabbhet. Kan vi räkna med att det dyker upp snabba GO lösningar där?

Ta allt inom AI

AI, eller specifikt ML, har rätt mycket standadiserat på Python. Det är nog också det bättre valet då mycket här handlar om att beskriva sina modeller och beskriva policy, något som i sig inte är prestandakritiskt samtidigt som Python ger väldigt kompakta och enkla lösningar.

De krävande sakerna görs oavsett språk bäst på GPU eller andra acceleratorer, d.v.s. man bygger specifika "kernels" som ändå inte kör på CPU -> spåkvalet för det som körs på CPU påverkar ändå inte prestanda på något relevant sätt.

I det fall man ändå kör på CPU är detta ett exempel när man behöver SIMD. Bra stöd för det får man endera via färdiga bibliotek (nog det vanligaste), genom att handknacka koden med t.ex. intrinsics (blir tyvärr specifikt inte bara för en viss CPU-arkitektur utan potentiellt även för en viss delmängd av ISA, kan vara vettigt vid behov men är själv glad så länge jag slipper) eller specialspråk designade för SIMD (de "vanliga" programspråket har typiskt rätt uselt SIMD-stöd) som t.ex. ISPC.

TL;DR är därför "nej, rent generellt är inte Go optimalt om man primärt är compute-bound, dess stora styrka infinner sig när man är I/O-bound". Spel är egentligen en variant av I/O-bound många gånger, flaskhalsen är hur bra man håller GPU sysselsatt samt hur bra man hanterar I/O (om det är multiplayer).

Edit: i.o.f.s. skulle Go:s concurrency-model göra det lite mindre utmanande att effektivt utnyttja fler CPU-kärnor i spel jämfört med vad som är fallet idag. I att det är utmanande idag är både en effekt av att C++ och C# är de vanligaste språken, men nog främst av att de populära spelmotorerna är nu så gamla att de designades innan multicore var normen och det är ett brutalt stort jobb att ändra arkitekturen utan att samtidigt slänga stora delar av alla assets, bibliotek etc på sophögen. ECS i Unity är lovande, men övergången går trögt. Epic jobbar på ett eget språk, Verse, specifikt för high-concurrency i spel.

Visa signatur

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