Interessant utveckling kring Java8 streams

Permalänk
Datavetare

Interessant utveckling kring Java8 streams

En av de viktigare tilläggen till Java8 är stöd för lambdauttryck. Jämfört med en del andra språk som också har stöd för lambdauttryck så har Java8 en del extra restriktioner när lambdauttrycken tar formen av en s.k. closure, begränsningen är att all data som från det kontext där man definierar sin closure måste vara konstant (d.v.s deklarerad final).

Fördelen med dessa restriktioner är att det möjliggör väldigt effektiv användning av flera CPU-kärnor när man jobbar med transformer på Java8-streams och detta är något som redan finns med i dagens Java8 plattformar.

En sak man pratat om sedan plattformar som använder JIT först såg dagens ljus är att de rent teoretisk har en stor fördel mot det traditionella sättet där man kompilerar allt i förväg, AOT, till underliggande maskinkod (Java använder i.o.f.s. också AOT, men i det steget går man till byte-kod och inte CPU-specifik maskinkod, JIT sker sedan från byte-kod till maskinkod vid körning).

I praktiken så har detta ett väldigt stort problem då det man som programmerare kan uttrycka i del flesta C-lika programspråk (som t.ex. Java) mappar ganska hårt mot hur man måste översätta det till maskinkod, i många fall skulle man kunna utnyttja HW-finesser som ökar prestanda men finns inget sätt för en JIT att garantera att sådan optimeringar är OK då massor med antaganden kring hur data används (något programmeraren ofta vet) inte har något representation i bytekoden (och en JIT vet därför inte om många optimeringar är OK eller ej). Resultatet är att man väldigt sällan kan generera kod som är bättre än AOT, ofta blir det sämre så AOT kan spendera mycket kraft på att hitta riktigt smarta optimeringar, något som skulle ge för stor "overhead" vid JIT.

Restriktionerna i lambdauttryck i Java8 ihop med "streams" API sätter upp ganska många ramar kring hur data får användas, vilket gör att man nu kan förutsätta ganska mycket om vilka optimeringar som är OK att utföra. Nu börjar det komma en del intressanta resultat från detta, dels har Intel jobbat på teknik som använder SSE/AVX till öka hastigheten när man jobba med parallella "steams" och dels jobbar AMD jobbat på liknande teknik där man i vissa fall kan avlasta sådana saker på GPU-delen.

Både SIMD och GPGPU är ju saker som i teorin har potential att ge mer beräkningskraft, men i praktiken blir användandet rätt begränsat då det har varit väldigt komplicerat (==dyrt) att använda sådan teknik i program, främst då man sällan kan förutsätta att all HW har de funktioner man behöver. Nu är det ganska begränsad användning av detta i Java (samt GPGPU tekniken är fortfarande under utveckling), men det är precis så här lätt det måste bli att använda sådana HW-finesser för att det ska bli standardfinesser i program.

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

Java har sina styrkor och svagheter precis som alternativen. Jag kodar både java och c#, det är bara verktyg för att nå ett mål men det går snabbare att koda bra och läsbarkod i c#.

Java saknar fortfarande mycket som finns i c# när jag kodar java, som tex await och linq, mm.

hyfsad jämförelse
http://stackoverflow.com/questions/610199/the-art-of-programm...

Lite godis för c# 6.0
http://blogs.msdn.com/b/csharpfaq/archive/2014/11/20/new-feat...

Edit: Sjukt störande att man inte i java kan pass object by reference utan att det alltid är by value. Ofta är det bra nog men ibland hade det underlättat kraftigt att köra pass by refrence, vilket java inte kan.

Visa signatur

Intel Core i7 8700K, MSI GeForce GTX 1080 Ti 11GB Gaming X, Samsung 960 EVO 1TB, MSI Z370 GAMING M5, Corsair 32GB (4x8GB) DDR4 3200MHz CL16 Vengeance, EVGA Supernova G3 850W

INTEL CORE I7 3930K 3.20GHZ 12MB S-2011, FRACTAL DESIGN MIDITOWER DEFINE R3, CORSAIR HX 1050W, ASUS RAMPAGE IV FORMULA, Asus STRIX GTX970, CORSAIR 16GB DDR3 DOMINATOR QUAD 1866MHZ CL9 (4X4GB) Ljud: ASUS Xonar D2X/XDT 7.1 | Elac 5.1 +förstärkare | Cambridge dacmagic plus | Astro gaming A40 | Sennheiser HD 650
You ask me if I have a god complex? Let me tell you something, I am god!

Permalänk
Medlem
Skrivet av IceDread:

Java har sina styrkor och svagheter precis som alternativen. Jag kodar både java och c#, det är bara verktyg för att nå ett mål men det går snabbare att koda bra och läsbarkod i c#.

Java saknar fortfarande mycket som finns i c# när jag kodar java, som tex await och linq, mm.

hyfsad jämförelse
http://stackoverflow.com/questions/610199/the-art-of-programm...

Lite godis för c# 6.0
http://blogs.msdn.com/b/csharpfaq/archive/2014/11/20/new-feat...

Inte för att hijacka tråden men wow, de där C# 6.0 grejerna låter så sjukt softa. Jobbar med C# och känner att flera av dessa grejerna kommer bli direkt användbara, framför allt null-conditional operatorn kommer spara en hel del redundanta kodrader.

Visa signatur

Fractal Design Define R5 | MSI Z97-GD65 Gaming | MSI Geforce GTX 970 Gaming 4G | Intel i5 4690k | Cooler Master Hyper 212 EVO | EVGA Supernova G2 750W | 2x8GB Corsair Vengeance Low Profile DDR3 1600Mhz | Samsung 850 EVO | Seagate 1TB SATA3.5

Permalänk
Datavetare
Skrivet av IceDread:

Java har sina styrkor och svagheter precis som alternativen. Jag kodar både java och c#, det är bara verktyg för att nå ett mål men det går snabbare att koda bra och läsbarkod i c#.

Java saknar fortfarande mycket som finns i c# när jag kodar java, som tex awaita och linq, mm.

I teorin är async/await ett väldigt välkommet tillägg då det möjliggör programmering på ett sätt som liknar Go/Erlang, det sättet är väldigt trevligt när man jobbar med system som utför väldigt mycket samtidig I/O men det man kör mellan operationerna inte drar supermycket CPU-kraft. Har själv lagt väldigt mycket kraft på att fundera på hur man kan få Go/Erlang modellen i C, men det är bara att konstatera att sättet Go/Erlang i sina definitioner beskriver hur anropsstacken ska hanteras är ett hårt krav för att det ska fungera bra.

Var därför väldigt förvånad när C# fick async/await för precis som C (och Java, C++, Pythot m.fl) saknar alla dessa "rätt" hantering av anropsstacken. Visar sig också att async/await har effektivitetsproblem just p.g.a. sättet man måste implementera det, så rekommendationen är att inte använda denna finess för högprestanda-servers.

Problemet kokar till stor del ner till detta:
"For asynchronous methods that actually yield execution (due to awaiting an object that’s not yet completed), the asynchronous method infrastructure needs to allocate a Task object to return from the method, as that Task serves as a unique reference for this particular invocation."
länk
Använder man detta i UI-sammanhang kommer ofta data finnas tillgängligt och man undviker denna kostnad, handlar det om I/O mot nätverk, filsystem, databaser etc så är en CPU så pass snabb att man i princip aldrig kommer ha data tillgängligt och därför måste ta de kostnader (som inte existerar i Erlang/Go) som beskrivs under "Know When Not to Use Async".

Java har överhuvudtaget ingen motsvarighet sett till koddesign, men funktionellt skulle man använda en kombination av "executors" och "NIO". Inte lika enkelt som Go/Erlang men resultat presterar lika bra.

Minst två väldigt viktiga saker saknas:

  • Moderna JVMer erbjuder sedan Java7 en långt mycket effektivare runtime-plattform än vad .NET har, framförallt kring multicore. Det spelar ingen roll om man utvecklar något som ska köras på en eller några få servers och den prestandakritiska biten inte ligger i den kod man själv skriver (väldigt vanligt i praktiken när det är en relationsdatabas med i mixen). Det är absolut kritiskt för program som ska köras på mindre enheter (Java ser ut att bli väldigt populärt inom IoT) eller när man kör i molnet där man i praktiken betalar i proportion mot använd CPU-tid. När programmet ska köras på många enheter så betyder mer effektivt program potential till billigare HW -> blir mycket pengar när det är många enheter. Omvänt så är inte utvecklingskostnaden alls lika viktig när den sätts i relation till kostnad per enhet.

  • Det absolut vanligaste användningsområdet för LINQ är LINQ-to-objects. Java8 streams och LINQ har en del fundamentala skillnader, i det specifika fallet LINQ-on-objects så är streams LINQ-done-right. Båda är i grunden rotade i den funktionella världen, inte så förvånande i fallet LINQs då en av dess skapare Eric Meijer jobbat väldigt mycket med funktionell programmering. Men de är rotade ur olika delar, streams är typisk map/reduce vilket är förklaringen till varför det fungerar så väl i parallella-system, även GPUer som inte nödvändigtvis ens är cache-koherenta. LINQ kommer i stället från konceptet monad funktioner (vilket möjliggör saker som JOIN, något som är väldigt icke-funktionellt). PLINQ är inte i närheten lika effektiv som parallel-streams och givet utgångspunkten de designat med borde det inte förvåna någon.

Då du kan både Java och C#, nämn en enda sak du kan göra med LINQ-to-objects som inte går att göra med ungefär samma arbetsinsats med Java-streams. Jämför sedan effektiviteten på din lösning, redan på en CPU-kärna är Java klart bättre, på multicore är den överlägsen.

Finns helt klart trevliga saker, men om det bara handlar om finesser och hur kort man kan skriva något skulle alla gått till Scala. Språk som Java och C är inte populära trots att de saknar mer avancerade språkkonstruktioner, de är populära p.g.a. detta.
Prio#1 för .NET borde vara att jobba på runtime-plattformen, framförallt om molnet eller IoT blir i närheten så stort som spås.

Skrivet av IceDread:

Edit: Sjukt störande att man inte i java kan pass object by reference utan att det alltid är by value. Ofta är det bra nog men ibland hade det underlättat kraftigt att köra pass by refrence, vilket java inte kan.

Tänk igen. Ihop med Javas inriktning mot effektiv parallellism och därmed mer funktionell modell enligt map/reduce så vore det fel väg att möjliggöra att returvärden också kan skickas via argument, för vilken annan vettig användning finns för pass by ref?

Varför irriterar du dig i så fall inte på att "extension methods" i C# inte har "dymanisk-dispatch", gillar man OOP borde det vara en riktig WTF! I Java diskuterande man väldigt läge att göra något liknande men alla förslag som hade "static-dispatch" som C# extension methods röstades ner då det finns en rad sätt att skjuta sig i fötterna. I Java8 kom till slut defender/default methods.

För att gå tillbaka till topic: det är definitivt stort om en så populär plattform som Java på ett vettigt sett man börja använda GPGPU (SIMD kan ses som ett specialfall av detta, vilket är precis vad t.ex. OpenCL gör). Utan något som är så pass enkelt lär det nog aldrig bli något en en relativt smal nisch.

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

Som jag skrev tidigare så anser jag att java och c# har bägge sina styrkor och svagheter, lätt att en post här kan uppfattas på fel sätt, men här följer lite åsikter. Jag tycker du har skrivit bra input men håller inte med om allt.

Skrivet av Yoshman:

I teorin är async/await ett väldigt välkommet tillägg då det möjliggör programmering på ett sätt som liknar Go/Erlang, det sättet är väldigt trevligt när man jobbar med system som utför väldigt mycket samtidig I/O men det man kör mellan operationerna inte drar supermycket CPU-kraft. Har själv lagt väldigt mycket kraft på att fundera på hur man kan få Go/Erlang modellen i C, men det är bara att konstatera att sättet Go/Erlang i sina definitioner beskriver hur anropsstacken ska hanteras är ett hårt krav för att det ska fungera bra.

Var därför väldigt förvånad när C# fick async/await för precis som C (och Java, C++, Pythot m.fl) saknar alla dessa "rätt" hantering av anropsstacken. Visar sig också att async/await har effektivitetsproblem just p.g.a. sättet man måste implementera det, så rekommendationen är att inte använda denna finess för högprestanda-servers.

Problemet kokar till stor del ner till detta:
"For asynchronous methods that actually yield execution (due to awaiting an object that’s not yet completed), the asynchronous method infrastructure needs to allocate a Task object to return from the method, as that Task serves as a unique reference for this particular invocation."
länk
Använder man detta i UI-sammanhang kommer ofta data finnas tillgängligt och man undviker denna kostnad, handlar det om I/O mot nätverk, filsystem, databaser etc så är en CPU så pass snabb att man i princip aldrig kommer ha data tillgängligt och därför måste ta de kostnader (som inte existerar i Erlang/Go) som beskrivs under "Know When Not to Use Async".

Java har överhuvudtaget ingen motsvarighet sett till koddesign, men funktionellt skulle man använda en kombination av "executors" och "NIO". Inte lika enkelt som Go/Erlang men resultat presterar lika bra.

Async, await är grymt, men måste givetvis användas rätt.
Exempelvis är await perfekt för en app /webb när du laddar olika delar av information + bilder att köra async. Lika så att skriva information / fel till en logg eller skicka meddelanden till en tjänst i bakgrunden.

Skrivet av Yoshman:

Minst två väldigt viktiga saker saknas:
[LIST]
[*]Moderna JVMer erbjuder sedan Java7 en långt mycket effektivare runtime-plattform än vad .NET har, framförallt kring multicore.

Nu kanske du missat att microsoft släpper roswell? Själv har jag inte hunnit testa eller läsa på om den så värst ännu, men den sägs vara kraftfull. Dessutom skall det vara ett öppet källkodsprojekt om jag inte mins fel vilket är rätt intressant.

Men jag måste också säga att jag håller med, multicore trådning har jag också uppfattningen om att java är effektivare på. Så har man en modul eller situation som är mycket prestanda intenstiv och tex inte är databehandling som troligen lämpar sig bäst i en databas där datan troligen redan är sparad, då är java bra.

Skrivet av Yoshman:

Det absolut vanligaste användningsområdet för LINQ är LINQ-to-objects. Java8 streams och LINQ har en del fundamentala skillnader, i det specifika fallet LINQ-on-objects så är streams LINQ-done-right. Båda är i grunden rotade i den funktionella världen, inte så förvånande i fallet LINQs då en av dess skapare Eric Meijer jobbat väldigt mycket med funktionell programmering. Men de är rotade ur olika delar, streams är typisk map/reduce vilket är förklaringen till varför det fungerar så väl i parallella-system, även GPUer som inte nödvändigtvis ens är cache-koherenta. LINQ kommer i stället från konceptet monad funktioner (vilket möjliggör saker som JOIN, något som är väldigt icke-funktionellt). PLINQ är inte i närheten lika effektiv som parallel-streams och givet utgångspunkten de designat med borde det inte förvåna någon.
[/LIST]
Då du kan både Java och C#, nämn en enda sak du kan göra med LINQ-to-objects som inte går att göra med ungefär samma arbetsinsats med Java-streams. Jämför sedan effektiviteten på din lösning, redan på en CPU-kärna är Java klart bättre, på multicore är den överlägsen.

För mig handlar det om enkelhet och läsbarhet och där tycker jag C# är bättre. Störst kostnad i de flesta system är ofta underhåll och att snabbt kunna läsa och ändra kod. Så beror lite på hur höga prestandakraven är, hur mycket data som skall behandlas.

Skrivet av Yoshman:

Finns helt klart trevliga saker, men om det bara handlar om finesser och hur kort man kan skriva något skulle alla gått till Scala. Språk som Java och C är inte populära trots att de saknar mer avancerade språkkonstruktioner, de är populära p.g.a. detta.
Prio#1 för .NET borde vara att jobba på runtime-plattformen, framförallt om molnet eller IoT blir i närheten så stort som spås.

Molnet, egentligen är det ju bara ett nytt namn på en gammal lösning, virtualiserad serverkraft. Det lämpar sig dock inte alltid bra, exempelvis har jag vissa kunder som pga säkerhet inte får eller vill lämna ut uppgifter till 3e part (tex amazon / ms). Men visst är det smidigt för vissa lösningar också.

Skrivet av Yoshman:

Tänk igen. Ihop med Javas inriktning mot effektiv parallellism och därmed mer funktionell modell enligt map/reduce så vore det fel väg att möjliggöra att returvärden också kan skickas via argument, för vilken annan vettig användning finns för pass by ref?

Generellt sett håller jag med om att by ref är onödigt och utgör också en risk.
Men i vissa situationer är det riktigt störande att sakna och kan vara kraftfullt.

Exempelvis håller jag just nu på att refakturera om en lösning med mycket logik redan skriven där objekt och delar av dem sätts om beroende på situation eller händelse och sätts om här och där.
Det är alltså legacy kod det handlar om.
Jag vill bryta ut rätt mycket kod från en gigant klass som i sig har ett gäng metoder som tar emot ny input och förändrar och utför arbeten på objekt som dessutom ibland sätts om helt, ibland efter en sparning eller inte...
Att i denna situation refakturera om och bryta ut logik till en annan klass utan att kunna använda ref.. är riktigt störande och slött. När jag blir klar har jag troligen inget större behov av ref, men just nu...

Sen finns det situationer med algoritmer där du vill sätta om ett gäng referenser och ändra pekarna hit och dit, då är ref smidigt.

Skrivet av Yoshman:

Varför irriterar du dig i så fall inte på att "extension methods" i C# inte har "dymanisk-dispatch", gillar man OOP borde det vara en riktig WTF! I Java diskuterande man väldigt läge att göra något liknande men alla förslag som hade "static-dispatch" som C# extension methods röstades ner då det finns en rad sätt att skjuta sig i fötterna. I Java8 kom till slut defender/default methods.

Det har jag inte sagt att jag inte stör mig på!
.net har sina brister också, inven tvekan om den saken. Men överlag går det snabbare att skriva tillräckligt effektiv kod som också är lätt att läsa och underhålla.

Som jag använder extensionmetoder så ofta är det mindre utökningar, som tex att utöka List med metoden addIfNotNull eller utöka med datumformateringar osv eller för att konvertera ett objekt jag fått från ett system till ett annat i det aktuella systemet.

Skrivet av Yoshman:

För att gå tillbaka till topic: det är definitivt stort om en så populär plattform som Java på ett vettigt sett man börja använda GPGPU (SIMD kan ses som ett specialfall av detta, vilket är precis vad t.ex. OpenCL gör). Utan något som är så pass enkelt lär det nog aldrig bli något en en relativt smal nisch.

Här hänger jag inte riktigt med, varken java eller c# är lämpligt för grafik. Eller tänkte du att du ville dra nytta av grafikkortetsberäkningskraft för att avhjälpa cpu?

Visa signatur

Intel Core i7 8700K, MSI GeForce GTX 1080 Ti 11GB Gaming X, Samsung 960 EVO 1TB, MSI Z370 GAMING M5, Corsair 32GB (4x8GB) DDR4 3200MHz CL16 Vengeance, EVGA Supernova G3 850W

INTEL CORE I7 3930K 3.20GHZ 12MB S-2011, FRACTAL DESIGN MIDITOWER DEFINE R3, CORSAIR HX 1050W, ASUS RAMPAGE IV FORMULA, Asus STRIX GTX970, CORSAIR 16GB DDR3 DOMINATOR QUAD 1866MHZ CL9 (4X4GB) Ljud: ASUS Xonar D2X/XDT 7.1 | Elac 5.1 +förstärkare | Cambridge dacmagic plus | Astro gaming A40 | Sennheiser HD 650
You ask me if I have a god complex? Let me tell you something, I am god!

Permalänk
Datavetare
Skrivet av IceDread:

Async, await är grymt, men måste givetvis användas rätt.
Exempelvis är await perfekt för en app /webb när du laddar olika delar av information + bilder att köra async. Lika så att skriva information / fel till en logg eller skicka meddelanden till en tjänst i bakgrunden.

Visst, men funktionen är ju logiskt sett som klipp och skuren för "concurrent programming" men p.g.a. implementationstekniska detaljer är det bara de simpla fallen som är rimligt att hantera. Blir då en svårt gränsdragning om man bör använda det eller ej, är det inte bättre att inse begränsningarna och konsekvent använda en metod som fungerar?

Skrivet av IceDread:

Nu kanske du missat att microsoft släpper roswell? Själv har jag inte hunnit testa eller läsa på om den så värst ännu, men den sägs vara kraftfull. Dessutom skall det vara ett öppet källkodsprojekt om jag inte mins fel vilket är rätt intressant.

Vet inte vad Roswell är, menar du Roslyn? Det verkar främst handlar om att möjliggöra mer saker i IDEer och att påverkar generering av CIL. Problemet med .NET är inte AOT kompilering till CIL, problemet är steget från CIL till maskinkod där .NET saknar en lång rad optimeringar moderna JVMer har.

Om du t.ex. definierar ett interface för väldigt verbose loggning så är det typiska angreppssättet i plattformar där AOT går direkt till maskinkod att man har ett debug och ett release bygge. Debug för felsökning och release för produktion, problemet är nu att om det börjar strula i produktion har man ofta ingen möjlighet att bara byta till debug. Massor med arbete har gjort på OS-nivå för att komma runt detta, t.ex. Linux OProfile/SystemTrap.

I Java kan man alltid köra "debug" versionen genom att variera den konkreta klassen av det som implementerar loggning. Det betyder att man måste ha "dynamisk-dispatch" vilket i tighta loopar betyder att anrop även till en tom metod kostar relativt mycket. Hotspot JVMer har länge haft optimering där de runtime inser att det finns bara en konkret implementation så ersätts alla anrop med direkta anrop, vilket möjliggör ännu optimeringar i form av inline-ing som resulterar i att den maskinkod som faktiskt körs när man har "release" loggning har absolut noll "overhead"! Så ur den aspekten är JVM-baserade språk extremt bra val för system som kräver hög tillgänglighet.

Moderna har JVM har en lång rad andra optimeringar som t.ex. att allokera kortlivade objekt på stacken (escape analysis) och möjlighet att ersätta maskininstruktioner för lås (som är dyra) med så länge en viss instans bara används av en enskild tråd (lock elision). Man undrar varför .NET aldrig fått något av detta.

Skrivet av IceDread:

För mig handlar det om enkelhet och läsbarhet och där tycker jag C# är bättre. Störst kostnad i de flesta system är ofta underhåll och att snabbt kunna läsa och ändra kod. Så beror lite på hur höga prestandakraven är, hur mycket data som skall behandlas.

Rekommendationer, baserat på erfarenhet från massor med forskning och statistik, i säkerhetskritiska system (eng. safety critical) säger alltid att man ska vara explicit då det visat sig att det är lättare att förstå kod som är lite mer verbose, framförallt får inget som ser ut som en trivial operation vara något annat än trivial. D.v.s. alla anrop som kan tänkas ske ska vara explicita, så en sak som "async" eller "kreativ" användning av operatoröverlagring (som måste vara ett problem i praktiken då både Java och Go explicit undvek denna finess) är inte något man kan ha då det ger upphov till en lång rad potentiella exekveringar som inte direkt uttrycks i koden.

D.v.s. får inte finnas uttryck som implicit ökar s.k. Cyclomatic complexity. C++ är ofta horribelt på denna punkt, Java och C är exempel på språk som i stort sätt saknar implicita anrop (C saknar det i språket kan utföras med preprocessor, Java har infört en del i Java8 )

Kod med låg cyclomatic complexity är typiskt väldigt lätt att förstå (korrekt), är väldigt lätt att testa och i många fall kan den bevisas vara korrekt.

Skrivet av IceDread:

Här hänger jag inte riktigt med, varken java eller c# är lämpligt för grafik. Eller tänkte du att du ville dra nytta av grafikkortetsberäkningskraft för att avhjälpa cpu?

Hela poängen med GPGPU är att kunna använda en modern GPU för andra sakar än grafik, AMD har ju satsat väldigt hårt på detta men problemet än så länge är att det är väldigt svårt (=dyrt) att skriva sådan kod med de verktyg som funnits. För att effektivt kunna köra sker på en GPU lär problemet vara på en viss form och finns ganska många restriktioner, i Java8 designades streams explicit för att fungera väl i parallella beräkningar vilket som bieffekt också betyder att många fall också är rimliga att köra på en GPU. SIMD ger inte riktigt samma boost, men kan å andra sidan användas i fler fall så båda projekten ovan är högst relevanta.

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

Varför vill man skriva beräkningstunga program i Java/C# istället för C/C++? Dagens C++-kompilatorer är förvånansvärt bra på SIMD:a kod, så ofta behöver man inte ens tänka på det själv. Att ha koll på var saker ligger i minnet är oftast nyckeln till prestanda och det har man väl inte direkt i Java/C#? Här är en bra illustration av det.

Permalänk
Datavetare
Skrivet av grovlimpa:

Varför vill man skriva beräkningstunga program i Java/C# istället för C/C++? Dagens C++-kompilatorer är förvånansvärt bra på SIMD:a kod, så ofta behöver man inte ens tänka på det själv. Att ha koll på var saker ligger i minnet är oftast nyckeln till prestanda och det har man väl inte direkt i Java/C#? Här är en bra illustration av det.

Finns väldigt många anledningar varför man väljer ett visst språk/plattform, en av de absolut vanligaste är att det man jobbar på är en utökning av något existerande och då är valet av språk/plattform redan gjort.

Helt klart finns det saker C och C++ är effektivare på och det har med CPU-cache att göra, ser däremot inte hur din illustration visar detta. Orsaken stavas främst "prefetch". I Java (och typ alla språk som använder sig av någon form av automatik GC) så representeras en array av objekt på detta sätt med C++ syntax (använder Javas "ordinary object pointer" klass som exempel)

vector<oop> arrary_of_some_ref_type;

D.v.s. minnet för objekten ligger inte direkt i C++ vector, där ligger pekare till objekten. För alla enklare formare av prefetcher kommer dessa inte lyckas att lura ut vilket minne som bör prefetchas, utrymmet där pekare ligger kommer effektivt prefetchas av den enklaste implementation. Inte ens de mest avancerade implementationer kommer gissa rätt på objekten om de inte råkar ligga "på rad" i minnet i samma ordning som deras pekare ligger.

Läser man spelbloggar och liknande hittar man kommenterar kring hur viktigt det är att tänka på sin minneslayout, vector<T> kan i prestanda kritiska situationer vara väldigt mycket snabbare än vector<T*> och i dessa situationer kan inte GC-språk som Java matcha språk med möjlighet till explicit minneshantering (C++11 har redan i standarden stöd för automatisk minneshantering, bygger på referensräkning men ger samma effekt i "collections").

Vill man vara säker på att ett program fungerar på alla x86_64 plattformar går det bara att använda SSE2, finns sätt runt detta genom att dynamisk lära sig vad som finns och ha flera olika implementationer. Men det ökar komplexiteten och därmed kostnaden, för GPGPU kommer den matrisen rätt snabbt bli ohållbar. Med JIT behöver utvecklaren inte tänka på denna komplexitet, det gör i stället plattformen. Det kommer inte ge riktigt samma effekt men det är väldigt mycket enklare och därmed långt sannolikare att det faktiskt används i praktiken.

Det skrivet, för det tråden handlar om är det totalt irrelevant.

Java8 streams handlar främst om detta:
"Second, how can we process really large collections efficiently? Ideally, to speed up the processing, you want to leverage multicore architectures. However, writing parallel code is hard and error-prone."

Skulle man göra ett liknande ramverk i C++ skulle jag gissa att det skulle vara något snabbare (dock mindre en en faktor 2) än Java-versionen. Språk med s.k. "tracing GC" (Java och C# har båda en sådan, referensräkning är inte en sådan) möjliggör användning av vissa klasser av datatyper som skalar extremt effektivt över CPU-kärnor, ett exempel är persistent data structure. Det är tekniskt möjligt att använda dessa typer av datastrukturer i språk utan "tracing GC", men det finns (än så länge i alla fall) inget sätt som inte leder till usel skalbarhet över CPU-kärnor vilket tar bort en av huvudpoängerna.

Rent praktiskt är det enklare att använda många CPU-kärnor i en plattform som JVM jämfört med C++. I specifika fall med utvecklare som har extremt detaljkunskap i cache-design och liknande är det ofta möjligt att matcha eller göra snabbare saker i C++. I det generella fallet är det bara att konstatera att språk med "tracing GC" och där runtime-plattformen i övrigt inte sätter käppar i hjulet så kan inte språk som C och C++ matcha skalbarheten över CPU-kärnor.

JVM-baserade språk dominerar totalt de plattformar som finns för "big data", ramverk som Storm och Spark. Sättet kod körs i dessa ramverk kräver intermediär kod likt Java bytekod och som användare kan man inte göra antagande om underliggande HW. Att då "automatiskt" kunna dra nytta av GPGPU och SIMD där det finns är ju guld värt.

Visa signatur

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