Programiranje

Primer za ohranjanje primitivov v Javi

Primitivi so del programskega jezika Java že od njegove prve izdaje leta 1996, vendar še vedno ostajajo ena bolj spornih jezikovnih lastnosti. John Moore je pomemben primer za ohranjanje primitivov v jeziku Java s primerjavo preprostih primerjalnih meril Java, tako s primitivi kot brez njih. Nato primerja delovanje Jave z delovanjem Scale, C ++ in JavaScript v določeni vrsti aplikacije, kjer primitivi bistveno razlikujejo.

Vprašanje: Kateri so trije najpomembnejši dejavniki pri nakupu nepremičnin?

Odgovorite: Lokacija, lokacija, lokacija.

Ta stari in pogosto uporabljeni pregovor naj bi pomenil, da lokacija popolnoma prevladuje nad vsemi drugimi dejavniki, ko gre za nepremičnine. V podobnem argumentu so trije najpomembnejši dejavniki, ki jih je treba upoštevati pri uporabi primitivnih vrst v Javi, zmogljivost, zmogljivost, zmogljivost. Med argumentom za nepremičnine in argumentom za primitiv sta dve razliki. Prvič, pri nepremičninah lokacija prevladuje v skoraj vseh situacijah, vendar se dobiček pri uporabi primitivnih vrst lahko zelo razlikuje od ene do druge aplikacije. Drugič, pri nepremičninah je treba upoštevati še druge dejavnike, čeprav so običajno manjši v primerjavi z lokacijo. Pri primitivnih vrstah je samo en razlog, da jih uporabimo - izvedba; in šele, če je aplikacija takšna, ki lahko koristi njihovi uporabi.

Primitivi ponujajo majhno vrednost za večino poslovnih in internetnih aplikacij, ki uporabljajo odjemalsko-strežniški programski model z bazo podatkov na zaledju. Toda uspešnost aplikacij, v katerih prevladujejo numerični izračuni, ima lahko veliko koristi od uporabe primitivov.

Vključitev primitivov v Javo je bila ena bolj spornih odločitev o oblikovanju jezika, kar dokazuje tudi število člankov in objav na forumih, povezanih s to odločitvijo. Simon Ritter je v svojem osrednjem nagovoru v Londonu novembra 2011 opozoril, da se v prihodnji različici Jave resno razmišlja o odstranitvi primitivov (glej diapozitiv 41). V tem članku bom na kratko predstavil primitive in Javin sistem dvojnega tipa. Z vzorci kode in preprostimi primerjalnimi preizkusi bom utemeljil, zakaj so Java primitivi potrebni za nekatere vrste aplikacij. Primerjal bom tudi zmogljivost Jave s Scalo, C ++ in JavaScript.

Merjenje zmogljivosti programske opreme

Učinkovitost programske opreme se običajno meri glede na čas in prostor. Čas je lahko dejanski čas delovanja, na primer 3,7 minute, ali vrstni red rasti glede na velikost vložka, na primer O(n2). Podobni ukrepi obstajajo tudi glede zmogljivosti prostora, ki se pogosto izraža v smislu porabe glavnega pomnilnika, lahko pa se razširi tudi na uporabo diska. Izboljšanje učinkovitosti običajno vključuje časovno-prostorski kompromis, saj spremembe za izboljšanje časa pogosto škodljivo vplivajo na prostor in obratno. Merjenje vrstnega reda rasti je odvisno od algoritma in prehod iz razredov ovojev na primitivne rezultate ne bo spremenil rezultata. Toda ko gre za dejansko časovno in prostorsko zmogljivost, uporaba primitivov namesto razredov ovitkov hkrati omogoča izboljšave tako v času kot v prostoru.

Primitivci v primerjavi s predmeti

Kot verjetno že veste, ali berete ta članek, ima Java sistem dvojnega tipa, ki ga običajno imenujemo primitivni tipi in tipi predmetov, pogosto pa okrajšani preprosto kot primitivi in ​​predmeti. V Javi je vnaprej določenih osem primitivnih vrst, njihova imena pa so rezervirane ključne besede. Pogosto uporabljeni primeri vključujejo int, dvojno, in logično. V bistvu so vse druge vrste v Javi, vključno z vsemi uporabniško določenimi tipi, vrste objektov. (Pravim "v bistvu", ker so tipi nizov nekoliko hibridni, vendar so bolj podobni objektnim vrstam kot primitivnim vrstam.) Za vsak primitivni tip obstaja ustrezen razred ovojnice, ki je objektni tip; primeri vključujejo Celo število za int, Dvojno za dvojno, in Logično za logično.

Primitivni tipi temeljijo na vrednosti, toda tipi predmetov temeljijo na referencah in v tem je moč in vir polemike primitivnih tipov. Za ponazoritev razlike upoštevajte spodnji izjavi. Prva deklaracija uporablja primitivni tip, druga pa razred zavijanja.

 int n1 = 100; Celo število n2 = novo celo število (100); 

Z uporabo avtoboksa, funkcije, dodane JDK 5, bi lahko drugo izjavo skrajšal na preprosto

 Celo število n2 = 100; 

vendar se osnovna semantika ne spremeni. Samodejno pošiljanje poenostavi uporabo razredov ovojev in zmanjša količino kode, ki jo mora napisati programer, vendar med izvajanjem ne spremeni ničesar.

Razlika med primitivnim n1 in ovojni predmet n2 ponazarja diagram na sliki 1.

John I. Moore, ml.

Spremenljivka n1 vsebuje celo število, vendar spremenljivka n2 vsebuje sklic na objekt in je objekt tisti, ki ima celoštevilčno vrednost. Poleg tega je predmet, na katerega se sklicuje n2 vsebuje tudi sklic na objekt razreda Dvojno.

Težava s primitivci

Preden vas poskušam prepričati v potrebo po primitivnih tipih, se moram zavedati, da se marsikdo ne bo strinjal z mano. Sherman Alpert v "Primitivnih vrstah, ki veljajo za škodljive" trdi, da so primitivi škodljivi, ker mešajo "proceduralno semantiko v sicer enoten objektno usmerjen model. Primitivi niso prvovrstni objekti, vendar obstajajo v jeziku, ki vključuje predvsem predmetov razreda. " Primitivi in ​​predmeti (v obliki razredov ovitkov) ponujajo dva načina za obravnavo logično podobnih vrst, vendar imajo v osnovi zelo različno semantiko. Na primer, kako je treba primerjati dva primerka za enakost? Za primitivne tipe uporabljamo == , vendar je za predmete najprimernejša izbira klic enako () metoda, ki za primitive ni mogoča. Podobno obstajajo različne semantike pri dodeljevanju vrednosti ali posredovanju parametrov. Tudi privzete vrednosti so drugačne; npr. 0 za int proti nič za Celo število.

Za več informacij o tej temi glejte prispevek Erica Bruna v blogu "Sodobna primitivna razprava", ki povzema nekatere prednosti in slabosti primitivov. Številne razprave o Stack Overflow se osredotočajo tudi na primitive, med drugim "Zakaj ljudje še vedno uporabljajo primitivne vrste v Javi?" in "Ali obstaja razlog, da namesto primitivov vedno uporabljamo Objekte ?." Programmers Stack Exchange gosti podobno razpravo z naslovom "Kdaj uporabiti primitive vs class v Javi?".

Izkoriščenost pomnilnika

A dvojno v Javi zaseda vedno 64 bitov v pomnilniku, vendar je velikost sklica odvisna od navideznega stroja Java (JVM). V mojem računalniku je nameščena 64-bitna različica sistema Windows 7 in 64-bitni JVM, zato referenca na mojem računalniku zaseda 64 bitov. Glede na diagram na sliki 1 bi pričakoval en sam dvojno kot naprimer n1 zaseda 8 bajtov (64 bitov) in pričakoval bi enega Dvojno kot naprimer n2 da zasede 24 bajtov - 8 za sklic na predmet, 8 za dvojno vrednost, shranjena v objektu, in 8 za sklic na objekt razreda za Dvojno. Poleg tega Java uporablja dodaten pomnilnik za podporo zbiranju smeti za tipe predmetov, ne pa tudi za primitivne tipe. Preverimo.

Z uporabo pristopa, podobnega pristopu Glena McCluskeyja v "Primitivni tipi Java v primerjavi z ovojnicami", metoda, prikazana v seznamu 1, meri število bajtov, ki jih zaseda matrika n-by-n (dvodimenzionalna matrika) dvojno.

Seznam 1. Izračun izkoriščenosti pomnilnika tipa double

 javni statični long getBytesUsingPrimitive (int n) {System.gc (); // prisilno zbiranje smeti dolgo memStart = Runtime.getRuntime (). freeMemory (); dvojno [] [] a = novo dvojno [n] [n]; // v matriko vstavimo nekaj naključnih vrednosti za (int i = 0; i <n; ++ i) {for (int j = 0; j <n; ++ j) a [i] [j] = Math. naključen(); } long memEnd = Runtime.getRuntime (). freeMemory (); vrni memStart - memEnd; } 

S spreminjanjem kode na seznamu 1 z očitnimi spremembami tipa (ni prikazano) lahko izmerimo tudi število bajtov, zasedenih z matriko n-by-n Dvojno. Ko v računalniku preizkusim ti dve metodi z uporabo matric 1000 na 1000, dobim rezultate, prikazane v spodnji tabeli 1. Kot je prikazano, različica za primitivni tip dvojno enaka nekaj več kot 8 bajtom na vnos v matriko, približno tako, kot sem pričakoval. Vendar pa različica za tip objekta Dvojno zahtevalo nekaj več kot 28 bajtov na vnos v matriko. Tako je v tem primeru izkoriščenost pomnilnika Dvojno je več kot trikrat večji izkoristek pomnilnika dvojno, kar ne bi smelo biti presenečenje za vsakogar, ki razume postavitev pomnilnika, prikazano na sliki 1 zgoraj.

Tabela 1. Izkoriščenost pomnilnika dvojnega in dvojnega

RazličicaSkupaj bajtovBajtov na vnos
Uporaba dvojno8,380,7688.381
Uporaba Dvojno28,166,07228.166

Izvedbena zmogljivost

Za primerjavo izvedbenih zmogljivosti primitivov in objektov potrebujemo algoritem, v katerem prevladujejo numerični izračuni. Za ta članek sem izbral množenje matrik in izračunam čas, potreben za pomnožitev dveh matric 1000 na 1000. Kodiral sem množenje matrike za dvojno na neposreden način, kot je prikazano v spodnjem seznamu 2. Čeprav obstajajo hitrejši načini za množenje matric (morda z uporabo sočasnosti), ta točka v tem članku ni res pomembna. Vse kar potrebujem je skupna koda v dveh podobnih metodah, ena z uporabo primitivnega dvojno in eno, ki uporablja razred ovitka Dvojno. Koda za množenje dveh matrik vrste Dvojno je točno tako kot v seznamu 2 z očitnimi spremembami tipa.

Seznam 2. Množenje dveh matrik tipa double

 javni statični double [] [] multiply (double [] [] a, double [] [] b) {if (! checkArgs (a, b)) vrže novo IllegalArgumentException ("Matrice niso združljive za množenje"); int nRows = a.length; int nCols = b [0] .length; dvojni [] [] rezultat = nov dvojni [nRows] [nCols]; for (int rowNum = 0; NumberNum <nRows; ++ rowNum) {for (int colNum = 0; colNum <nCols; ++ colNum) {dvojna vsota = 0,0; za (int i = 0; i <a [0] .length; ++ i) sum + = a [rowNum] [i] * b [i] [colNum]; rezultat [številka vrstice] [števec] = vsota; }} vrni rezultat; } 

Z dvema metodama sem večkrat pomnožil dve matriki 1000 na 1000 v računalniku in izmeril rezultate. Povprečni časi so prikazani v tabeli 2. Tako je v tem primeru izvedbena zmogljivost dvojno je več kot štirikrat hitrejši od Dvojno. To je preprosto prevelika razlika, da bi jo lahko prezrli.

Tabela 2. Izvedbena zmogljivost dvojne in dvojne

RazličicaSekunde
Uporaba dvojno11.31
Uporaba Dvojno48.48

Primerjava SciMark 2.0

Doslej sem z enostavnim merilom množenja matrik dokazoval, da lahko primitivi prinesejo bistveno večje računalniške zmogljivosti kot predmeti. Za okrepitev svojih trditev bom uporabil bolj znanstveno merilo. SciMark 2.0 je merilo Java za znanstveno in numerično računalništvo, ki ga ponuja Nacionalni inštitut za standarde in tehnologijo (NIST). Prenesel sem izvorno kodo za to primerjalno točko in ustvaril dve različici, prvotno različico z uporabo primitivov in drugo različico z razredi ovitkov. Za drugo različico sem zamenjal int s Celo število in dvojno s Dvojno da dobite popoln učinek uporabe razredov ovitkov. Obe različici sta na voljo v izvorni kodi tega članka.

prenesi Benchmarking Java: Prenesite izvorno kodo John I. Moore, Jr.

Primerjalno merilo SciMark meri zmogljivost več računskih rutin in poroča o sestavljenem rezultatu v približno Mflops (milijoni operacij s plavajočo vejico na sekundo). Tako so za to merilo primernejše večje številke. V tabeli 3 so podane povprečne sestavljene ocene iz več zagonov vsake različice tega primerjalnega preizkusa v mojem računalniku. Kot je prikazano, so bile zmogljivosti med izvajanjem obeh različic merila SciMark 2.0 skladne z zgornjimi rezultati množenja matric, saj je bila različica s primitivi skoraj petkrat hitrejša od različice z razredi ovitkov.

Tabela 3. Učinkovitost izvajanja merila SciMark

Različica SciMarkZmogljivost (Mflops)
Uporaba primitivov710.80
Uporaba razredov ovitkov143.73

Videli ste nekaj različic programov Java, ki opravljajo numerične izračune z uporabo domačega in bolj znanstvenega merila. Kako pa se Java primerja z drugimi jeziki? Zaključil bom s hitrim pregledom primerjave Java-ove uspešnosti s tremi drugimi programskimi jeziki: Scala, C ++ in JavaScript.

Benchmarking Scala

Scala je programski jezik, ki deluje na JVM in je vse bolj priljubljen. Scala ima enoten sistem tipa, kar pomeni, da ne razlikuje med primitivi in ​​predmeti. Po Eriku Osheimu iz Scalovega numeričnega razreda (Pt. 1) Scala uporablja primitivne tipe, kadar je to mogoče, vendar bo po potrebi uporabila predmete. Podobno v opisu Scala's Arrays Martina Oderskega piše, da "... matrika Scala Matrika [Int] je predstavljen kot Java int [], an Matrika [dvojno] je predstavljen kot Java dvojno [] ..."

Torej, ali to pomeni, da bo Scala-in enotni sistem tipov imel zmogljivosti, primerljive s primitivnimi tipi Java? Pa poglejmo.

$config[zx-auto] not found$config[zx-overlay] not found