Java 9 on kymmenen kertaa nopeampaa!

Törmäsin mielenkiintoiseen ilmiöön taannoin. Valmistelin esitystä suorituskyvystä ja Java muistimallista, ja ajoin eräänlaista benchmark sovellusta Dockerin avulla eri virtuaalikoneissa. Sovelluksen ideana on sisältää aika typerästi kirjoitettua mutta varsin tavanomaista Java-koodia. Silmukoita, ehtolauseita, merkkijonojen käsittelyä, laskentaa, kokoelmien käsittelyä, ja kellottaa paljonko kaikkeen menee aikaa. Tällä voidaan myös nähdä nykyaikaisen virtuaalikoneen itseään optimoiva vaikutus – kun ajo jatkuu, nopeus yleensä kasvaa. Tässä on sovelluksen mittaustuloksia Oracle Java 6 Docker kontissa ajettuna:

Screenshot 2016-10-16 13.19.54.png

Eli, yhden kierroksen aika on n. 84 sekuntia, laskien 82 sekuntiin jahka virtuaalikone vähän ”lämpenee” eli käytännössä jit-kääntää enemmän koodia ja tekee muita tarvittavia optimointeja.

 

Tässä on OpenJDK+Java 8 ajotulokset:

Screenshot 2016-10-16 13.23.31.png

Kuten tuloksista näkyy, uudempi virtuaalikone optimoi usein tehokkaammin. Tässä kierrosajat pyörivät n. 62 sekunnin pinnassa – Java 6 verraten on irronnut noin 20 sekuntia, tai 1/4 suoritusajasta. Paras kierrosaika oli jopa 52 sekuntia. Aika hyvä tulos!

Kokeillaanpa G1 roskankeruualgoritmilla:

Screenshot 2016-10-16 13.35.12.png

Oops! Vaikka G1 on teoriassa kauneinta ja uusinta mitä roskankeruualgoritmeihin tulee, se ei ole joka ongelmaan optimaalinen ratkaisu. Suoritusajat vaihtelevat 98 ja 104 sekunnin välillä ja ovat nousemaan päin. Tällä sovelluksella ja tämän koneen muistilla tässä tuli takapakkia, huonompi suorituskyky. Varmaan tästä syystä G1 ei ole vielä oletusalgoritmina vaan pitää erikseen kytkeä päälle lisäparametrilla -XX:+UseG1GC.

Java 9 julkaistaan vasta pitkällä ensi vuonna. Siitä on kuitenkin jo prereleaseja liikkeellä, ja jopa Docker virtuaalikuva. Tämän ansiosta on lasten leikkiä ajaa sama koodi Java 9:llä. Tulokset tässä:

Screenshot 2016-10-16 13.36.26.png

WUT? Sama koodi, tekee saman asian, sama koneympäristö ja resurssit. Suoritusajat vaihtelevat 9 ja 12 sekunnin  välillä. Karkeasti ottaen noin kymmenen kertaa nopeampaa kuin useimmat muut testiajot, ja yli viisi kertaa nopeampaa kuin paras tulos tähän asti.

Jotain on muuttunut. Mitä, en tiedä vielä. Epäilen että yksi tekijä voi olla Jigsaw moduulimallit. Toinen tekijä lienee, että on taas opittu tunnistamaan joku negatiivinen koodaustapa, ja optimoimaan sen suoritus. Tulokset tuskin ovat yleispäteviä, ne pätevät lähinnä tähän tyypilliseen koodiesimerkkiin mitä käytin, ja tähän ympäristöön. Docker välissä voi myös vaikuttaa jotain, tuskin kuitenkaan paljoa. Niin tai näin, koodi otti taas kerran hurjan tehokkuushypyn. Tätä herkkua olisi luvassa ensi vuonna.

Virtuaalikoneiden ihanuus on siinä, että nautit kaikista edistysaskelista, ilman koodimuutoksiakin. IBM nimesi juuri oman open source JDK 9 versionsa JIT-kääntäjän Testarossaksi, joten veikkaisin että sieltä on myös suurta hyvyyttä tulossa.

p.s. Docker on ihana keksintö!

p.p.s. Niin on Cathode terminaalikin :p

 

Tykkäätkö erityisesti hitaasta, monimutkaisesta ja vaikeasta? Älä missään nimessä siirry Java 8:aan!

Jep, provosoivaan otsikkoon vähän sisältöä. Olen jo vuoden päivät opiskellut Java 8 version tulevia muutoksia, kyseessä on tosiaan iso pläjäys joka koodaajaa hyödyntävää tavaraa. Ne summautuvat yhteen tehokkuudeksi – monissa paikoissa voi nyt tehdä koodaustyöt siten että koodirivejä syntyy vähemmän – koodi on luettavampaa ja näin ollen kalliita huolimattomuusvirheitä on vähemmän – ja kaiken lisäksi uudella tapaa tehty koodi voi skaalautua moniydinprosessoireissa ihan uudella tapaa. Turha odotella paria vuotta kun kaikesta tästä voi nauttia NYT. Koodaajille tätä ei ole tarpeen paljoa perustella, mutta koko projektin kannalta näkyy jo selvää rahaa kun siirtyy käyttämään fiksumpia uusin piirtein varustettuja alustoja. Jos pyörität vielä Java versioita 5 tai 6, on peiliin katsomisen aika. 😉

Java 1.8 julkaistaan näillä näkymin 18.3. lopullisena versiona. Siitä on jo toimivaa Release Candidate versiota tarjolla. Jokainen kehitysympäristö tukee jo enemmän tai vähemmän sen käyttöä. Ja mikä tärkeintä, Tieturilla on tarjolla heti koulutusta jossa saat kickstartin uusiin piirteisiin ohjatusti ja vinkkejä parhaista käytännöistä ja myös sudenkuopista joita voi tulla vastaan.

Java 8 uudet piirteet (Huhti ja toukokuussa toteutus):
http://www.tieturi.fi/kurssit/kurssi.html?course=85000345

Tärkeimmät tulossa olevat uutuudet ovat tietysti Lambdat, joka toimii kevyenä porttihuumeena, ja sitten Streamit, joissa piilee se kova kama. Sen ohella vietellään kovasti kohden funktionaalista ohjelmointia joka on testattavampaa ja skaalautuvampaa. Vanha kunnon isoisän Javahan alkaa tässä näyttämään ihan Scalalta! Kun soppaan lisätään vielä uusi päivämääräkirjasto, joka on Jodatimen mukaan tehty ja ensimmäistä kertaa alkaa olla kohtuullisen mehukas – on aika painavat syyt päivittää itsensä ja ympäristönsä ASAP.

Tein vähän mallikoodia omaan käyttöön testatakseni piirteitä. Lähtötilanteena oli käydä läpi vähän tietokannastamme löytyvää kurssitarjontaa, ja tehdä siihen erilaisia hakuja. Otin tahallisesti laajan otoksen kaikesta mitä löytyi, joten datan laadussa on hyvin tyypillisiä heikkouksia, mm. puuttuvia kenttiä, ja tein siihen liittyvää perinteistä hyvin rumaa mutta myös hyvin tyypillistä imperatiivista koodia. Kuvaavaa on että koodin monimutkaisuuden vuoksi ehdin tehdä parikymmentä kertaa jonkin virheen joka testatessa kävi ilmi. Huolimattomuusvirheet voivat isommassa projektissa tulla äkkiä hyvin kalliiksi etenkin jos testaus ei ole automatisoitua, kattavaa, säännöllistä  ja nopeaa. Lähdin alunperin testailemaan suorituskykyä, mutta matkalla löytyikin muuta. Katsotaanpa mitä tapahtui.

Lähtötilanne: Kannasta on haettu tauludataa, joka on talletettu JSON olioihin. Pääohjelma lataa ne levyltä, ja prosessoi ne läpi, tulostaen lopuksi käsitellyt tiedot. Pääohjelma on tämän näköinen:

public static void main(String[] args)
    throws IOException, JAXBException {
  List<Course> courses = loadCourses("test.json");
  long begin = System.nanoTime();
  List<Course> validPublicTrainings = filterAndFixCourses(courses);
  long end = System.nanoTime();

  // Print values out to control how it works 
  for (Course course : validPublicTrainings) { 
    System.out.println(course);
  }

  System.out.println("Number of filtered courses: " 
    + validPublicTrainings.size()); 
  System.out.println("Time spent: " + (end - begin) / 1000000.0 
    + "ms"); 
}

Melko yksinkertaista tavaraa, siis. Kaikki mehukas tapahtuu filterAndFixCourses() funktiossa, sen tehtävä on suodattaa kurssit jotka ovat julkisia, valideja, ja pistää ne päivämäärän mukaan järjestykseen. Ongelmia tuottaa, että osassa dataa arvoja puuttuu ja ne ovat null-arvoj (Kaiken tämän voisi tehdä kannassa mutta tämä ei ole kantakoodauksen artikkeli 😉

Tältä näyttää perinteiseen imperatiiviseen tyyliin kirjoitettu varsin ruma ja optimoimaton (mutta myös varsin tyypillinen) esimerkki:

public static List<Course> filterAndFixCourses(
    List<Course> courseList) {
  // Get all trainings that are public and valid
  List<Course> validPublicTrainings = new ArrayList<>();
  for (Course course : courseList) {
    if (course.getType() != null && course.getType() == 3 
        && course.getStatus() != null && course.getStatus() == 3) {
      if (course.getBeginDate() != null 
          && course.getEndDate() != null) {
        // Fix dates by adding +10 hours, 36000ms
        course.setBeginDate(new Date(
           course.getBeginDate().getTime() + 36000));
        course.setEndDate(new Date(
           course.getEndDate().getTime() + 36000));
      }
     validPublicTrainings.add(course);
    }
  }

  // Sort by begindate and enddate
  // Descending order for dates, meaning that newest dates go first
  // Null values go to bottom of list
  Comparator<Course> c = new Comparator<Course>() {
    @Override
      public int compare(Course o1, Course o2) {
        if (o1.getBeginDate() == null 
            && o2.getBeginDate() == null) {
          return 0;
        } else if (o1.getBeginDate() == null) {
          return 1;
        } else if (o2.getBeginDate() == null) {
          return -1;
        } else if (o1.getEndDate() == null 
            && o2.getEndDate() == null) {
          return 0;
        } else if (o1.getEndDate() == null) {
          return 1;
    } else if (o2.getEndDate() == null) {
      return -1;
    } else if (o1.getBeginDate().before(o2.getBeginDate())) {
      return 1;
    } else if (o1.getBeginDate().after(o2.getBeginDate())) {
      return -1;
    } else if (o1.getEndDate().before(o2.getEndDate())) {
      return 1;
    } else if (o1.getEndDate().after(o2.getEndDate())) {
      return -1;
    } else {
      return 0;
    }
   }
  };
  Collections.sort(validPublicTrainings, c);
  return validPublicTrainings;
}

Rumaa? Jep, elämä on. Tuota voisi stilisoida moninkin tavoin mutta totuus on, että tuollaista koodia löytyy tuotantojärjestelmistä ympäri maailman. Kuten Venkat Subramaniam sanoi luennoissaan, kun tuota kirjoittelee päivän, tuntee olonsa likaiseksi ja kaipaa suihkua.

Miltäpä tämä sitten näyttäisi Java 8 avulla? Kokeillaanpa uudestaan. Ei mikromanageroida ulkopuolelta mitä pitäisi tehdä, vaan käytetään Lambdoja ja Streameja, ja annetaan algoritmit suoraan Streamille, näin:

public static List<Course> filterAndFixCourses(
    List<Course> courseList) {
  return courseList.stream()
    .filter(c -> c.getType() != null)
    .filter(c -> c.getType() == 3)
    .filter(c -> c.getStatus() != null)
    .filter(c -> c.getStatus() == 3)
    .sorted((o1, o2) -> {
       if (o1.getBeginDate() == null 
            && o2.getBeginDate() == null) {
         return 0;
       } else if (o1.getBeginDate() == null) {
         return 1;
       } else if (o2.getBeginDate() == null) {
         return -1;
       } else if (o1.getEndDate() == null 
           && o2.getEndDate() == null) {
         return 0;
       } else if (o1.getEndDate() == null) {
         return 1;
       } else if (o2.getEndDate() == null) {
         return -1;
       } else if (o1.getBeginDate().before(o2.getBeginDate())) {
         return 1;
       } else if (o1.getBeginDate().after(o2.getBeginDate())) {
         return -1;
       } else if (o1.getEndDate().before(o2.getEndDate())) {
         return 1;
       } else if (o1.getEndDate().after(o2.getEndDate())) {
         return -1;
       } else {
         return 0;
       }
     })
     .map((c) -> fixDate(c))
     .collect(Collectors.toList());
}

Parempi? Mmmhmm… Tavallaan, imperatiivisen tyylin mikromanagerointi on poistunut, ja voisi sanoa että koodi on alkuun jopa ymmärrettävämpää. Mutta monimutkaiset asiat ovat edelleen monimutkaisia ja sotkuisia, vaikeasti ymmärrettäviä. Mutta meillä on käsissämme funktionaalinen ohjelmointityyli. Mitäpä jos.. käyttäisimme funktioita hoitamaan likaiset työt? Sama uusiksi funktioreferensseillä Java 8 tapaan:

public static List<Course> filterAndFixCourses(
    List<Course> courseList) {
  return courseList.stream()
    .filter(App::typeThreeIsValid)
    .filter(App::statusThreeIsValid)
    .sorted(App::sortByDate)
    .map(App::fixDate)
    .collect(Collectors.toList());
}

Aaaaaivan! Koodirivien määrä rupsahti 53:sta kuuteen – tai jos tarkkoja ollaan niin yhteen, tuohan on kaikki samaa return lauseketta eli one-lineri. Tietysti osa koodia siirtyi vain funktioihin joka aina kannattaisi tehdä. Kuten yllä näkyy, hyvä idea nimetä funktiot dokumentatiiviseen tapaan, vähän kuin hyvissä yksikkötesteissä.

Mutta luettavuuden voisi väittää parantuneen, kun ylläolevaa koodia lukee, sitä voi jopa ymmärtää. Kenties jopa kuukauden päästä palatessaan asiaan.

Mitäpä tapahtui suorituskyvylle? Kellotin vähän millisekunteja. Imperatiivinen koodiesimerkki rusikoi 100 000 tietueen massaa n. 27ms verran. Funktionaalinen Java 8 versio käytti samaan aikaa n. 54ms. Hetki, miten tässä näin kävi? Eikö tämän pitänyt olla nopeampaa? No ei. Skaalautuvampaa, kyllä. Nopeushävikkiä voi syntyä esim. immutable-olioiden kopioinnista, ja toisaalta tässä ovat operaatiot niin nopeita että ylimääräisten askelien overhead jää suuremmaksi kuin mikään tehosäästö.

Mutta, tämänhän piti skaalautua mukavasti moniydinprosessoreille. Tässä koneessani on niitä 8, ajetaanpa testi uusiksi tällä koodilla:

// Best version of Java 8 features with parallel execution
 public static List<Course> filterAndFixCourses(
    List<Course> courseList) {
    return courseList.parallelStream()
       .filter(App::typeThreeIsValid)
       .filter(App::statusThreeIsValid)
       .sorted(App::sortByDate)
       .map(App::fixDate)
       .collect(Collectors.toList());
}

Kuten huomaat, ainoa mitä piti muuttaa oli korvata stream() parallelStream() kutsulla. Nyt taustalla käytetään Fork&Joinia ja hajoitetaan ja hallitaan operaatioita, ajaen ne rinnakkain. Mitenkäs suuri nopeushyöty saadaan tästä?

Turpiin tuli. Nyt aikaa meni 90ms, eli lähes neljä kertaa hitaampaa. Mikä meni vikaan?

Kuten aiemminkin, overhead iski. Thread poolin pystytys ja töiden skedulointi vie suoritusaikaa. Tässä esimerkissä laskennallinen tehtävä on edelleen triviaalin nopea ja siksi sen tekeminen rinnakkain ei hyödytä tarpeeksi. Eli jos nopeutta hakee, tämä kannattaisi edelleen tehdä mutkattomammin imperatiivisesti. Ytimien lisääminenkään tuskin auttaisi, koska overhead.

Mutta.. entäpä jos työ olisi raskaampaa.. Nykyään dataa haetaan usein verkon yli, tai tietokannasta, useissa erissä. Edellämainitun koodin ongelmana alkoi olemaan muistin riittäminen jos tietueita olisi vaikka miljoona. Looginen ratkaisu olisi hakea sitä sopivammissa viipaleissa kannasta tai verkon yli.

Entä jos siinä menisi esim. 10ms per kierros aikaa? Simuloidaan koodiin pientä verkkoviivettä, näin:

try {
 Thread.sleep(10);
 } catch (InterruptedException ex) { }

Joka kierroksella menee hetki aikaa, joko laskenta on raskasta tai haetaan jotain verkon yli tai kannasta. 18793 tehtävää (viive on vain suodatetuilla alkioilla), 10ms viivettä per tehtävä. Nyt tilanne muuttuu, imperatiiviselta koodilta kestää: 194 045ms tehdä työ. Ei kovin suuri yllätys, 100k kertaa 10 on miljoona. Entä miten suoriutuu parallel Stream Java 8 versio? Suoritusaika on 25272ms, eli imperatiivisen koodin aika jaettuna ytimien määrällä plus overhead. Kahdeksan ytimen kone ajaa operaatiot lähes 8x nopeammin, ja skaalautuu jos ytimiä onkin 16, 32, jne..

Yhteenveto: Java 8 uudet piirteet ovat uutta arsenaalia ohjelmoijan työkalupakissa. Jo selkeyden ja ongelmanratkaisun välineinä niitä ei ole varaa olla käyttämättä. Ne eivät automaattisesti ole ylivoimaisia suoritusnopeudessa – mutta tehtävissä joissa rinnakkaisuudesta voi olla hyötyä, ne tuovat ylivertaista ja ohjelmoijalle helppoa skaalautuvuutta. Ja vielä on yksi syy lisää opiskella tämä NYT: Ellet opiskele, pian voi pamahtaa silmille Java-koodia jota et enää pysty ymmärtämään. Muistatko miten kävi Java 5 kohdalla? 😉

Pari tärppiä vielä loppuun. Streamien debuggaus voi olla hankalaa – ja tuki vielä huteraa kehitysvälineissä. Apuun tulee peek()-funktio, jolla voit kurkata prosessoinnin sisään. Esim. näin:

return courseList.parallelStream()
  .filter(App::typeThreeIsValid)
  .filter(App::statusThreeIsValid)
  .sorted(App::sortByDate)
  .peek(System.out::println)
  .map(App::fixDate)
  .collect(Collectors.toList());

Ei siis hullumpaa uudistusta. Matkaa on vielä Scalan hienouksiin mutta nyt meillä on jotain käyttökelpoista joka taas inspiroi koodaamista vuosiksi eteenpäin.

Kuka kerää roskasi?

Jep, tämä ei ole menneen Vihreä IT- trendin artikkeli, vaan sanoilla leikittelyä jälleen kerran. Java- koodaajat tietävät mitä tarkoittaa automaattinen roskienkeruu, garbage collection. Se tarkoittaa että koodaaja ei erityisesti varaa eikä vapauta muistia vaan käyttää olioita, ja ympäröivä virtuaalikone/framework hoitaa tämän perusaskaren. Tämä taas johtaa helpompaan koodiin ja parempaan toimintavarmuuteen – noin keskiverrosti. Java-kielen ja -alustan keskeinen piirre on ollut automaattinen roskankeruu.

Monet koodaajat eivät kuitenkaan pohdi roskankeruun toimintaa sen enempää. Meidän optimointikursseillamme opetetaan sen syvempää ymmärrystä, seurantaa ja analysointia, ja myös sen perusteella millainen koodi on tehokasta ja millainen ei. Tyypillinen roskankeruualgoritmi nimittäin tekee ’stop the world’-paussin ja pysäyttää koodin aina roskia kerättäessä. Jos roskia syntyy paljon, pausseja on siis paljon ja ne ovat pidempiä. Pahimmillaan huono sovellus voi käyttää suurimman osan aikaa roskankeruussa, ja vain murto-osan koodia ajaessa.

Roskankeruuhun liittyy paljon säätöjä ja mitättömin niistä ei ole roskankeruualgoritmin valinta. Vaihtoehtoja on monia, ja hyvä algoritmi riippuu siitä mitä koodi tekee ja miten koodi toimii, mutta myös siitä millaiset fyysiset resurssit ympäristössä on käytössä. Oli osittain yllätys – osittain ei- nähdä tuore tutkimus jossa mainittiin että suurin osa ihmisistä ajelee Javaa vaativissakin ympäristöissä oletusasetuksin. Osittain johtuu siitä että ei ole tietoa paremmasta. Varmasti osittain myös siitä että ei ole tarvetta – rauta on verrattain halpaa ja resursseja piisaa useimmiten tuhlattavaksi. Eniten optimoinnista hyötyvät ne, joiden softan pitää toimia äärimmäisen niukissa olosuhteissa – tai sitten äärimmäisen runsaissa, kuten Twitterin 64-gigaiset konehirviöt.

Joka tapauksessa, Performance Zone tekemän tutkimuksen mukaan 13% on säätänyt virtuaalikonealgoritmia, 87% jättää sen oletuksille. Joka on hyvä, oletusalgoritmi on useimmiten oivallinen. Mutta kun tarjolla on mm. rinnakkaisroskankeruuta, lyhyiden paussien algoritmeja, ja uusin G1, vaihtoehtojakin on. Toki oletuksetkin vaihtuvat virtuaalikoneen version ja ympäröivän koneen luokituksen mukaan (Server/Workstation).

Mielenkiintoista on myös että uusin ja kaunein G1 on myös kiistanalaisin – tutkimuksessa ihmetelläänkin miten niin monet ovat sen jo päälle kääntäneet, vaikka se ei tuo välttämättä useimmissa tapauksissa yhtään hyötyjä suorituskykyyn vaan pikemminkin hidastaa – myös omiin kokemuksiin perustuen. Kuinka ollakaan, Performance Labin kaverit ovat kenties unohtaneet mikä on G1 mallin todellinen hyöty: Roskankeruupaussien minimointi. Se on suunniteltu ennen muuta massiivisille heap muisteille joissa perinteinen roskankeruu voi pysäyttää toiminnan järkyttävän pitkiksi ajoiksi. Joka ei ole serverilläkään hyvä asia, saati sitten itseohjautuvassa älyautossa. Pienemmilläkin heap muisteilla sillä voi pyrkiä tasoittamaan roskankeruupausseja – joka toki yleensä tekee roskankeruusta kaiken kaikkiaan hitaampaa – mutta tasaisempaa. Try it!

Tässä lähdelinkki tutkimukseen:

http://java.dzone.com/articles/what-garbage-collector-are-you

Ja jos se optimointi kiinnostaa niin tässä härski puffi oman firman koulutukseen:

http://www.tieturi.fi/kurssit/kurssi.html?course=83901618

Blogeissahan ei saa nykyisellään kauhean vaivihkaisesti mainostaa joten pitäisi varmaan suurentaa tuota fonttia ja kirjoittaa disclaimer: Kaikki mitä täällä kirjoitan saattaa johtaa addiktioon seurata blogia jatkossakin, sekä mahdolliseen ostopäätökseen itseään kehittävien koulutusten suhteen – joko meiltä tai muilta 😉

http://www.kkv.fi/Page/d45bee63-a3a7-49a1-b8c8-5d6fa02a6dba.aspx

Java suorituskyvyn tuunaamisesta

Törmäsin erinomaiseen artikkelisarjaan koskien Java suorituskyvyn tuunaamista. Aihe on ollut lähellä sydäntäni, sitä on tullut koulutettua ja konsulttihommissakin olen asiakkaita auttanut tiristämään virtuaalikoneesta kaikki tehot irti ja miettimään mitä hölmöilyjä EI SAA koodissa tehdä. Kuitenkin tuntuu että joka kuukausi aiheesta oppii lisää, ja aina välineet voisivat olla myös parempia mitä käyttelee.

 

Kirjoittelin jo taannoin New Relic tuotteesta joka osui omaan tutkaan JavaOne seminarissa vuonna 2011. Samaan sarjaan kuuluvat JRebel, JFrog ja Artifactory, hyviä vimpaimia kaikki. Näistä New Relic perustuu reaaliaikaiseen suorituskyvyn mittaukseen, monitorointiin ja analysointiin, ja vaikuttaisi rahan arvoiselta palikalta. Itselle ei ole ollut tarpeen kuitenkaan sitä ostella, koska omissa systeemeissä ei ole ihmeempiä suorituskykyhaasteita koskaan ollut, ja vähäiset mysteerit on pystytty ratkomaan ihan jconsolella tai visualvm:llä sekä logeilla.

 

Pari omaa kulmakiveä mitä tulee Java suorituskykyyn:

– Koodin optimoinnilla on lopulta varsin vähäinen merkitys, se mikä merkitsee on arkkitehtuuri ja se että koodissa ei hölmöillä eli turhaan haaskata suorituskykyä.

– Java on pääsääntöisesti nykyään C++ kielen kanssa tasalinjalla monessa operaatiossa, mutta on yhä alueita joissa se voi olla tolkuttoman hidasta. Pääsääntö siihen miten olla hölmöilemättä on ymmärtää Java muistimalli. Kiitos automaattisen roskankeruun tärkeintä on olla aiheuttamatta tarpeetonta roskankeruuta. Etenkään jos roskankeruualgoritmi on ns. Stop-The-World malli joka paussittaa oman koodin. Joissain mittaroimissani koodipohjissa vietetään aikaa roskankeruussa enemmän kuin itse tuotantokoodissa – pahimmillaan suhde on ollut jopa 99% vs 1%! Roskankeruuta voi välttää pääsääntöisesti miettimällä olioiden elinkaarta ja raskautta, ja tekemällä niistä tarvittaessa pitkäikäisempiä.

– Parhaiten optimoituu koodi joka on kirjoitettu hyvin yksinkertaisesti. Java virtuaalikone optimoi ajon aikana ja optimointi perustuu siihen miten koodi normaalisti toimii. Jos olet koodannut kummallisia virityksiä, virtuaalikone ei ehkä kykene koodia optimoimaan oikein tai voi jopa optimoida väärin. Muista siis optimoinnin kolme ensimmäistä sääntöä: Älä optimoi koodatessasi, älä optimoi koodatessasi, ÄLÄ OPTIMOI KOODATESSASI! Tee koodia joka on selkeää ja ymmärrettävää ensin, ja optimoi vasta tarpeen tullen ja mittaustuloksiin perustuen.

– Virtuaalikonetta voi tuunata. Sitä ei kuitenkaan kannata tehdä sokkona vaan taas pitäisi tietää mitä muistin sisällä tapahtuu. Turn on the headlights! Virtuaalikoneessakin on kosolti kytkimiä joilla sieltä saa tietoa muistista, optimoinneista ja ajankäytöstä. Löytyy myös visuaalisia työkaluja jotka ovat hyviä tiivistämään näkymiä suuresta datamassasta. Java ei nopeudu välttämättä lisäämällä isompi muistikampa ja parempi prosessori – se voi pahimmillaan jopa hidastua siitä (kiitos roskankeruun toiminnan).

Tuossapa taas viikon ärhentelyt. Java optimointi ei ole vaikeaa, mutta se vaatii osaamista, tietoa, työkaluja, ja hieman loogista päättelyä. Se vaatii myös testi edellä menemistä, koska joskus arvaus tai teoria osoittautuu vääräksi ja voi jopa hidastaa tai muuttaa sovellusta raskaammaksi tarpeettomasti.

Joka tapauksessa, tässä muiden ajatuksia aiheista ja välineistä. JRebelillä on tietysti oma lehmä ojassa mutta mielenkiintoista luettavaa silti. Meillä asiaa puidaan mm. Tehokas Java-kurssilla:

Tehokas Java-kurssi Tieturilla

Become a Java GC Expert artikkelisarja

 

 

Kiihdytä Android kehitystäsi!

Jep, puuhaillessani Android III kurssin kanssa törmäsin hyvään artikkeliin. Uusimmissa Android kehitysympäristöissä on nimittäin pari lisäpiirrettä joilla saa emulaattoriin vauhtia, tietyissä olosuhteissa.

Android emulaattorihan on julmetun hidas. Se emuloi dalvik bytecode virtuaalikonetta linuxin päällä joka pyörii emuloidun arm prosessorin päällä. Tukkoista on. Intel on kuitenkin toimittanut virtuaalikoneen myös, jossa päästään lähemmäs rautaa jos ollaan Windows tai Mac alustoilla. Tähän saa erityisesti buustia kun asentaa ja kytkee HAXM managerin päälle, ja kytkee myös rautakiihdytetyn GPU emulaation päälle.  Jos koneessa ei ole tehoja tai hyvää kiihdytettyä grafiikkakorttia näistä ei juuri ole apua. Mutta jos näitä löytyy, saat kaiken irti.

Prosessi on helppo ja testattu: Avaa Android SDK asennustyökalu ja asenna sieltä Intel Atom virtuaalikone sekä HAXM (Intel Hardware Accelerated Execution Manager). Huom! Image on saatavana vain parille API versiolle, tällä hetkellä API 15 eli Android 4.03 ja API 10 eli Android 2.3.3 – mutta niillä pääsee jo pitkälle!

HAXM ei asennu automaattisesti, vaan asennusohjelma latautuu android sdk kansioon extras/intel alle. Asenna se. Jos asennus epäonnistuu, rauta ei ole yhteensopivaa.

 

Homma hoidossa? Hienoa, nyt voit palata Eclipseen tai muuten vain AVD manageriin ja luoda uuden virtuaalikoneen. Valitse siihen API Level 15 tai 10, Intel prossu, ja GPU kiihdytys päälle. Ei ole huono idea myöskään pajauttaa gigaa paria muistia jos sitä piisaa, tähän tapaan:

 

Homma hoidossa? GPU emulaatio päällä snapshotit eivät pelaa – mutta en käytä itse niitä muutenkaan epävakauden vuoksi ja nyt pitäisi käynnistymisen olla nopeampaa.

Ja perään se perinteinen ’lääkintöhallituksen varoitus’ – jos tapat koneesi asennellessasi näitä en kanna yhtikäs mitään vastuuta. Ja android softan testauksessa emulaattori on vain yksi väline, pitäisi aina testata aidolla raudalla. Mutta onhan tuo penteleen mukavaa että ensikierroksen testaus on nopeutunut silmin nähtävästi! Ensikäynnistys vei hetkisen edelleen, mutta toistuvat käynnistykset.. Omalla koneellani puhutaan n. 10 sekunnin ajasta. Ehkä 20 sekuntia että kaikki on käynnissä. Ja käyttö on yhtä nopeaa ja miellyttävää kuin Galaxy III:lla 😉 Tämä on hyvä vinkki myös jos puuhailet paljon mobiili web-sivustojen testausta ja puljaat virtuaalikoneiden kanssa. Puhutaan noin 2-3 kertaisesta nopeutuksesta vähintään jo ennestään nopeutuneeseen myllyyn.

 

Jep, ja loppuun lähdelinkki alkuperäiseen artikkeliin:

http://www.developer.com/ws/android/development-tools/haxm-speeds-up-the-android-emulator.html

Ja Android 3 kurssi on valmis ja kimaltelee jo kurssitarjonnassa sisältäen kaikkea hauskaa kuten NFC ja Face Detection 😉

http://www.tieturi.fi/kurssit/kurssi.html?course=85000210&category=Mobiiliteknologiat