Programavimas

„Primityvų laikymo Java“ atvejis

Pradinukai buvo „Java“ programavimo kalbos dalis nuo pat jos išleidimo 1996 m., Tačiau vis dėlto jie išlieka viena iš prieštaringesnių kalbos ypatybių. Johnas Moore'as pateikia tvirtą pagrindą laikyti „Java“ primityvus lygindamas paprastus „Java“ etalonus tiek su primityvais, tiek be jų. Tada jis lygina „Java“ našumą su „Scala“, C ++ ir „JavaScript“ tam tikro tipo programose, kur primityvūs ženkliai skiriasi.

Klausimas: Kokie trys svarbiausi faktoriai perkant nekilnojamąjį turtą?

Atsakymas: Vieta, vieta, vieta.

Šis senas ir dažnai naudojamas posakis turi reikšti, kad vieta visiškai dominuoja dėl visų kitų nekilnojamojo turto veiksnių. Panašiame argumente trys svarbiausi veiksniai, į kuriuos reikia atsižvelgti naudojant primityvius tipus „Java“, yra našumas, našumas, našumas. Yra du skirtumai tarp argumento dėl nekilnojamojo turto ir argumento dėl primityvumo. Pirma, kalbant apie nekilnojamąjį turtą, vieta dominuoja beveik visose situacijose, tačiau efektyvumo padidėjimas naudojant primityvius tipus gali labai skirtis. Antra, kalbant apie nekilnojamąjį turtą, reikia atsižvelgti į kitus veiksnius, nors jie, palyginti su vieta, paprastai yra nedideli. Naudojant primityvius tipus, yra tik viena priežastis juos naudoti - spektaklis; ir tada tik tuo atveju, jei jų naudojimas gali būti naudingas.

„Primitive“ teikia mažai naudos daugumai su verslu susijusių ir interneto programų, kurios naudoja kliento-serverio programavimo modelį su duomenų baze vidinėje pusėje. Tačiau programų, kuriose vyrauja skaitiniai skaičiavimai, našumas gali būti labai naudingas naudojant primityvius dalykus.

Primityvių kalbų įtraukimas į „Java“ buvo vienas iš prieštaringesnių sprendimų dėl kalbos dizaino, ką patvirtina straipsnių ir forumo pranešimų, susijusių su šiuo sprendimu, skaičius. Simonas Ritteris savo „JAX London“ 2011 m. Lapkričio mėn. Pagrindiniame pranešime pažymėjo, kad rimtai svarstoma apie primityvų pašalinimą būsimoje „Java“ versijoje (žr. 41 skaidrę). Šiame straipsnyje aš trumpai supažindinsiu su primityviaisiais ir „Java“ dvigubo tipo sistema. Naudodamas kodo pavyzdžius ir paprastus etalonus pateiksiu klausimą, kodėl tam tikrų tipų programoms reikalingi „Java“ pradmenys. Taip pat palyginsiu „Java“ našumą su „Scala“, „C ++“ ir „JavaScript“.

Programinės įrangos našumo matavimas

Programinės įrangos našumas paprastai matuojamas pagal laiką ir erdvę. Laikas gali būti tikrasis veikimo laikas, pvz., 3,7 minutės, arba augimo tvarka pagal įvesties dydį, pvz O(n2). Panašios priemonės yra ir kosmoso našumui, kuris dažnai išreiškiamas pagrindinės atminties naudojimu, tačiau taip pat gali apimti disko naudojimą. Našumo gerinimas paprastai apima kompromisą tarp laiko ir erdvės, nes pokyčiai, siekiant pagerinti laiką, dažnai daro žalingą poveikį erdvei ir atvirkščiai. Augimo eilės matavimas priklauso nuo algoritmo, o perėjimas iš pakavimo klasių į primityvų rezultato nepakeis. Tačiau kalbant apie faktinį laiko ir erdvės našumą, vietoj įvyniojimo klasių primityvių variantų naudojimas vienu metu gali pagerinti laiką ir erdvę.

Pirmykščiai prieš daiktus

Kaip jūs tikriausiai jau žinote, ar skaitote šį straipsnį, „Java“ turi dvigubo tipo sistemą, paprastai vadinamą primityviais tipais ir objektų tipais, dažnai sutrumpintai tiesiog kaip primityvius ir objektus. Iš anksto „Java“ iš anksto apibrėžti aštuoni primityvūs tipai ir jų pavadinimai yra rezervuoti raktiniai žodžiai. Dažniausiai naudojami pavyzdžiai tarpt, dvigubaiir loginis. Iš esmės visi kiti „Java“ tipai, įskaitant visus vartotojo apibrėžtus tipus, yra objektų tipai. (Aš sakau „iš esmės“, nes masyvų tipai yra šiek tiek hibridiniai, tačiau jie yra daug panašesni į objektų tipus nei į primityvius tipus.) Kiekvienam primityviam tipui yra atitinkama apvalkalo klasė, kuri yra objekto tipas; pavyzdžiai apima Sveikasis skaičius dėl tarpt, Dvigubai dėl dvigubaiir Būlio dėl loginis.

Pirmykščiai tipai yra pagrįsti verte, tačiau objektų tipai yra orientaciniai, todėl juose slypi ir primityvių tipų galia, ir ginčų šaltinis. Norėdami paaiškinti skirtumą, apsvarstykite dvi toliau pateiktas deklaracijas. Pirmojoje deklaracijoje naudojamas primityvus tipas, o antrame - vyniojimo klasė.

 int n1 = 100; Sveikasis skaičius n2 = naujas sveikasis skaičius (100); 

Naudodamas „Autoboxing“ funkciją, pridėtą prie JDK 5, antrąją deklaraciją galėčiau sutrumpinti

 Sveikasis skaičius n2 = 100; 

bet pagrindinė semantika nesikeičia. „Autoboxing“ supaprastina „wrapper“ klasių naudojimą ir sumažina kodą, kurį programuotojas turi rašyti, tačiau jis nieko nepakeičia vykdymo metu.

Skirtumas tarp primityvaus n1 ir įvyniojimo objektas n2 pavaizduota 1 paveiksle pavaizduota schema.

Johnas I. Moore'as, jaunesnysis

Kintamasis n1 turi sveiko skaičiaus vertę, bet kintamasis n2 yra nuoroda į objektą, o būtent objektas turi sveiko skaičiaus vertę. Be to, objektas, kuriuo remiasi n2 taip pat yra nuoroda į klasės objektą Dvigubai.

Primityvų problema

Prieš bandydamas įtikinti jus apie primityvių tipų poreikį, turėčiau pripažinti, kad daugelis žmonių su manimi nesutiks. Shermanas Alpertas knygoje „Primityvūs tipai, laikomi kenksmingais“ teigia, kad primityviai yra kenksmingi, nes jie maišo „procedūrinę semantiką į kitaip vienodą į objektą orientuotą modelį. Pirmykščiai nėra pirmos klasės objektai, tačiau jie egzistuoja kalboje, kuri pirmiausia apima klasės objektai “. Pirmykščiai ir objektai (įvyniojimo klasių pavidalu) pateikia du logiškai panašių tipų valdymo būdus, tačiau jų pagrindinė semantika yra labai skirtinga. Pvz., Kaip lyginti du atvejus? Pirmykščių tipų atveju naudojamas == operatorius, bet objektams pageidaujamas pasirinkimas yra skambinti lygi () metodas, kuris nėra primityvių pasirinkimas. Panašiai, skiriant vertes ar perduodant parametrus, egzistuoja skirtinga semantika. Net numatytosios vertės skiriasi; pvz., 0 dėl tarpt prieš niekinis dėl Sveikasis skaičius.

Norėdami gauti daugiau informacijos apie šią problemą, skaitykite Erico Bruno dienoraščio įrašą „Šiuolaikinė primityvi diskusija“, kuriame apibendrinami kai kurie primityvių privalumai ir trūkumai. Daugybė diskusijų apie „Stack Overflow“ taip pat yra nukreiptos į primityvius dalykus, įskaitant „Kodėl žmonės vis dar naudoja primityvius„ Java “tipus? ir "Ar yra priežastis visada naudoti objektus, o ne primityvius?" Programuotojai „Stack Exchange“ rengia panašią diskusiją „Kada Java naudoti primityvų vs klasę?“.

Atminties panaudojimas

A dvigubai „Java“ atmintyje visada užima 64 bitus, tačiau nuorodos dydis priklauso nuo „Java“ virtualiosios mašinos (JVM). Mano kompiuteryje veikia 64 bitų „Windows 7“ versija ir 64 bitų JVM, todėl mano kompiuteryje esanti nuoroda užima 64 bitus. Remdamasis 1 paveiksle pateikta diagrama, tikiuosi vieno dvigubai toks kaip n1 užimti 8 baitus (64 bitai), ir aš tikiuosi, kad tai bus vienas Dvigubai toks kaip n2 užimti 24 baitus - 8 už nuorodą į objektą, 8 už dvigubai reikšmė, saugoma objekte, ir 8 - nuoroda į klasės objektą Dvigubai. Be to, „Java“ naudoja papildomą atmintį, kad palaikytų šiukšlių surinkimą objektų tipams, bet ne primityviems. Patikrinkime.

Taikant metodą, panašų į Gleno McCluskey'o metodą „Java primityviųjų tipų ir apvyniojimų“ atveju, 1 sąraše pateiktas metodas matuoja baitų, kuriuos užima n-by-n matrica (dvimatė masyvas), skaičių. dvigubai.

Išvardinimas 1. Dvigubo tipo atminties panaudojimo skaičiavimas

 public static long getBytesUsingPrimitives (int n) {System.gc (); // priversti šiukšlių surinkimą ilgas memStart = Runtime.getRuntime (). freeMemory (); dvigubas [] [] a = naujas dvigubas [n] [n]; // į matricą įdėkite keletą atsitiktinių reikšmių (int i = 0; i <n; ++ i) {for (int j = 0; j <n; ++ j) a [i] [j] = Math. atsitiktinis (); } long memEnd = Runtime.getRuntime (). freeMemory (); grįžti memStart - memEnd; } 

Keisdami kodą 1 sąraše, atlikdami akivaizdžius tipo pakeitimus (neparodyti), taip pat galime išmatuoti baitų, kuriuos užima n-by-n matrica, skaičių Dvigubai. Kai išbandau šiuos du metodus savo kompiuteryje naudodamas 1000 x 1000 matricas, gaunu 1 lentelėje nurodytus rezultatus. Kaip parodyta, primityvaus tipo versija dvigubai prilygsta šiek tiek daugiau nei 8 baitams už įrašą matricoje, maždaug to, ko tikėjausi. Tačiau objekto tipo versija Dvigubai reikėjo šiek tiek daugiau nei 28 baitų vienam įrašui matricoje. Taigi šiuo atveju atminties panaudojimas Dvigubai yra daugiau nei tris kartus didesnis nei atminties panaudojimas dvigubai, kuri neturėtų būti staigmena tiems, kurie supranta atminties išdėstymą, pavaizduotą 1 paveiksle.

1 lentelė. Dvigubos ir dvigubos atminties panaudojimas

VersijaIš viso baitųBaitai už įrašą
Naudojant dvigubai8,380,7688.381
Naudojant Dvigubai28,166,07228.166

Veikimo laikas

Norint palyginti pradmenų ir objektų vykdymo laiką, reikia algoritmo, kuriame vyrauja skaitiniai skaičiavimai. Šiam straipsniui pasirinkau matricos dauginimą ir apskaičiuoju laiką, reikalingą dviem 1000-1000 matricoms padauginti. Užkodavau matricos dauginimą dvigubai paprastu būdu, kaip parodyta toliau pateiktame 2 sąraše. Nors gali būti greitesnių būdų įgyvendinti matricos dauginimą (galbūt naudojant lygiagretumą), šis punktas iš tikrųjų nėra svarbus šiam straipsniui. Viskas, ko man reikia, yra bendras kodas dviem panašiais metodais, viename naudojant primityvųjį dvigubai ir vienas - naudojant vyniojimo klasę Dvigubai. Dviejų tipo matricų dauginimo kodas Dvigubai yra lygiai taip pat kaip 2 sąraše su akivaizdžiais tipo pakeitimais.

Išvardinimas 2. Padauginus dvi dvigubo tipo matricas

 viešasis statinis dvigubas [] [] padauginti (dvigubas [] [] a, dvigubas [] [] b) {if (! checkArgs (a, b)) išmeta naują „IllegalArgumentException“ („Dauginti nesuderinamos matricos“); int nRows = a.ilgis; int nCols = b [0] .ilgis; dvigubas [] [] rezultatas = naujas dvigubas [nRows] [nCols]; už (int rowNum = 0; rowNum <nRows; ++ rowNum) {for (int colNum = 0; colNum <nCols; ++ colNum) {dviguba suma = 0,0; už (int i = 0; i <a [0]. ilgis; ++ i) suma + = a [eilutėNum] [i] * b [i] [colNum]; rezultatas [rowNum] [colNum] = suma; }} grąžinimo rezultatas; } 

Paleidau du metodus, kad kelis kartus padauginčiau dvi 1000-1000 matricas savo kompiuteryje ir matavau rezultatus. Vidutiniai laikai parodyti 2 lentelėje. Taigi šiuo atveju vykdomasis laikas dvigubai yra daugiau nei keturis kartus greitesnis nei Dvigubai. Tai tiesiog per didelis skirtumas, kad nepaisytum.

2 lentelė. Dvigubo ir dvigubo veikimo laikas

VersijaSekundės
Naudojant dvigubai11.31
Naudojant Dvigubai48.48

„SciMark 2.0“ etalonas

Iki šiol aš naudoju vieną paprastą matricos daugybos etaloną, kad pademonstruočiau, jog primityviai gali duoti žymiai didesnį skaičiavimo našumą nei objektai. Norėdamas sustiprinti savo teiginius, naudosiu labiau mokslinį etaloną. „SciMark 2.0“ yra „Java“ etalonas moksliniam ir skaitmeniniam skaičiavimui, kurį gali gauti Nacionalinis standartų ir technologijos institutas (NIST). Atsisiunčiau šio etalono šaltinio kodą ir sukūriau dvi versijas: pradinę versiją naudodamas primityvius, o antrąją - naudodamas pakavimo klases. Dėl antrosios versijos aš pakeičiau tarpt su Sveikasis skaičius ir dvigubai su Dvigubai kad gautumėte visą įpakavimo klasių naudojimo efektą. Abi versijos pateikiamos šio straipsnio šaltinio kode.

atsisiųsti „Benchmarking Java“: atsisiųskite šaltinio kodą John I. Moore, Jr.

„SciMark“ etalonas matuoja kelių skaičiavimo procedūrų našumą ir pateikia bendrą balą apytiksliais Mflops (milijonai slankiojo kablelio operacijų per sekundę). Taigi šiam etalonui geriau naudoti didesnius skaičius. 3 lentelėje pateikiami vidutiniai sudėtiniai balai iš kelių kiekvieno šio etalono varianto bandymų mano kompiuteryje. Kaip parodyta, dviejų „SciMark 2.0“ etalono versijų vykdymo trukmės rezultatai atitiko aukščiau pateiktus matricos daugybos rezultatus, nes versija su primityvais buvo beveik penkis kartus greitesnė nei versija, naudojanti įvyniojimo klases.

3 lentelė. „SciMark“ etalono vykdymo laikas

SciMark versijaSpektaklis (Mflops)
Naudojant primityvius710.80
Naudojant įvyniojimo klases143.73

Jūs matėte keletą „Java“ programų variantų, atliekančių skaitinius skaičiavimus, naudodami tiek savą, tiek labiau mokslinį etaloną. Bet kaip „Java“ lyginama su kitomis kalbomis? Baigsiu greitai apžvelgdamas „Java“ našumą su trijų kitų programavimo kalbų: „Scala“, „C ++“ ir „JavaScript“.

„Scala“ palyginimas

„Scala“ yra programavimo kalba, naudojama JVM ir, atrodo, vis labiau populiarėja. „Scala“ turi vieningo tipo sistemą, tai reiškia, kad ji neskiria primityvų ir objektų. Pasak Eriko Osheimo iš „Scala“ skaitmeninių tipų klasės (1 p.), „Scala“, jei įmanoma, naudoja primityvius tipus, tačiau prireikus naudos objektus. Panašiai Martino Odersky aprašyme apie „Scala“ masyvus sakoma, kad „...„ Scala “masyvas Masyvas [vid.] yra vaizduojamas kaip „Java“ int [], an Masyvas [dvigubas] yra vaizduojamas kaip „Java“ dvigubai [] ..."

Taigi ar tai reiškia, kad „Scala“ vieningo tipo sistemos vykdymo laikas bus panašus į primityvius „Java“ tipus? Pažiūrėkime.