Pari viikkoa sitten aloitimme sarjan tarkoituksena on kaivaa syvemmälle JavaScript ja miten se todella toimii: ajattelimme, että tuntemalla rakennuspalikoita JavaScript ja miten he tulevat pelaamaan yhdessä voit kirjoittaa parempaa koodia ja sovellukset.,
ensimmäinen viesti sarjassa keskittynyt tarjoamaan yleiskuvan moottori, runtime ja kutsupino. Tämä toinen viesti sukeltaa Googlen V8 JavaScript-moottorin sisäosiin. Annamme myös muutamia nopeita vinkkejä siitä, miten kirjoittaa parempia JavaScript-koodia – parhaita käytäntöjä kehitystiimimme Sessionstackissa seuraa tuotetta rakennettaessa.
Yleistä
JavaScript-moottori on ohjelma, tai tulkki, joka suorittaa JavaScript-koodin., JavaScript-moottori voidaan toteuttaa tavallisena tulkki, tai just-in-time compiler, joka kokoaa JavaScript bytecode jossain muodossa.,/li>
miksi V8-moottori luotiin?
Googlen rakentama V8-moottori on avoimen lähdekoodin ja kirjoitettu C++: lla. Tätä moottoria käytetään Google Chromen sisällä. Muista moottoreista poiketen V8: aa käytetään kuitenkin myös suositussa solmussa.js runtime.
V8 oli ensimmäinen suunniteltu lisäämään suorituskykyä JavaScript-toteutuksen sisällä web-selaimet., Nopeuden saamiseksi V8 kääntää JavaScript-koodin tehokkaammaksi konekoodiksi tulkin sijaan. Se kokoaa JavaScript-koodin konekielelle suorituksen toteuttamalla JIT (Just-In-Time) kääntäjä, kuten monet modernit JavaScript-moottorit, kuten SpiderMonkey tai Sarvikuono (Mozilla). Suurin ero tässä on se, että V8 ei tuota bytecodea tai mitään välikoodia.
V8: lla oli ennen versiota 5 kaksi kääntäjää
.,9 V8 tuli ulos (julkaistiin aiemmin tänä vuonna), moottori käytetty kaksi kääntäjät:
- täysi-codegen — yksinkertainen ja erittäin nopea kääntäjä, joka on tuotettu yksinkertainen ja suhteellisen hidas kone-koodin.
- Kampiakselin — monimutkaisempi (Just-In-Time) optimoimalla kääntäjä, joka tuotti erittäin optimoitu koodi.,l>
- lanka ei mitä voit odottaa: hakee koodin, kääntää sen ja suorittaa sen sitten
- Siellä on myös erillinen thread laatimista varten, niin, että lanka voi jatkaa suorittamista, kun entinen on optimoimalla koodi
- Profiloija lanka, joka kertoo runtime on, mitä menetelmiä käytämme paljon aikaa niin, että Kampiakselin voi optimoida niitä
- muutaman kierteet käsitellä roskienkerääjä pyyhkäisee
Kun ensin suorittamalla JavaScript-koodia, V8 hyödyntää täysi-codegen, joka suoraan käännettynä jäsentää JavaScript konekielelle ilman mitään muutosta., Näin se voi aloittaa koneen koodin suorittamisen erittäin nopeasti. Huomaa, että V8 ei käytä keskitason bytecode-edustusta näin poistaen tulkin tarpeen.
kun koodisi on toiminut jo jonkin aikaa, profilointikierre on kerännyt tarpeeksi tietoa kertoakseen, mikä menetelmä kannattaa optimoida.
seuraavaksi Kampiakselin optimoinnit alkavat toisesta langasta. Se kääntää JavaScript abstrakti syntaksipuun korkean tason staattinen yhden tehtävän (SSA) edustus kutsutaan Vetyä ja yrittää optimoida, että Vety kuvaaja. Suurin osa optimoinneista tehdään tällä tasolla.,
alleviivaus
ensimmäinen optimointi alleviivataan mahdollisimman paljon koodia etukäteen. Inline on prosessi, jossa kutsupaikka (koodi, jossa funktiota kutsutaan) korvataan kutsutun funktion rungolla. Tämä yksinkertainen vaihe mahdollistaa seuraavat optimoinnit olla mielekkäämpiä.
Piilotettu luokka
JavaScript on prototyyppi-pohjainen kieli: ei ole luokkia ja objekteja luodaan käyttämällä kloonaus prosessi., JavaScript on myös dynaaminen ohjelmointikieli, joka tarkoittaa, että ominaisuuksia voidaan helposti lisätä tai poistaa kohteen sen jälkeen, kun esimies.
Useimmat JavaScript-tulkit käyttää sanakirjaa-kuin rakenteet (hash function based) tallentaa sijainnin objektin ominaisuus arvot muistiin. Tämä rakenne tekee haetaan kiinteistön arvoa JavaScript laskennallisesti kalliimpaa kuin se olisi ei-dynaaminen ohjelmointikieli, kuten Java tai C#., Java, kaikki objektin ominaisuudet määräytyvät kiinteän objektin asettelua ennen kokoaminen ja voi dynaamisesti lisätä tai poistaa suorituksen (no, C# on dynaaminen tyyppi, joka on toinen aihe). Seurauksena, arvoja ja ominaisuuksia (tai osoittimia niitä ominaisuuksia) voidaan tallentaa jatkuvana puskuri muistissa kiinteä-offset keskenään. Offsetin pituus voidaan helposti määrittää kiinteistötyypin perusteella, kun taas tämä ei ole mahdollista Javascriptissä, jossa Kiinteistötyyppi voi muuttua ajonaikana.,
koska sanakirjojen käyttäminen objektin ominaisuuksien löytämiseen muistista on hyvin tehotonta, V8 käyttää sen sijaan eri menetelmää: piiloluokkia. Piilotetut luokat toimivat samalla tavalla kuin kiinteä objekti kaavoista (luokat) käyttää kieliä kuten Java, paitsi ne luodaan suorituksen. Nyt, katsotaanpa, mitä he todella näyttävät:
function Point(x, y) {
this.x = x;
this.y = y;
}var p1 = new Point(1, 2);
Kun ”new Piste(1, 2)” vetoaminen tapahtuu, V8 luo uuden piilotetun luokan nimeltä ”C0”.,
N ominaisuudet on määritetty Vaiheessa vielä, joten ”C0” on tyhjä.
kerran ensimmäinen lausuma ” tämä.x = x” on toteutettu (sisällä ”Kohta” – toiminto), V8 luo toinen piilotettu luokka nimeltään ”C1”, joka perustuu ”C0”. ”C1” kuvaa paikkaa muistissa (suhteessa olioosoittimeen), josta ominaisuus x löytyy., Tässä tapauksessa ”x” on tallennettu offset 0, mikä tarkoittaa, että kun katselet kohta objekti muistissa jatkuvana puskuri, ensimmäinen offset vastaavat omaisuus ”x”. V8 on myös päivittää ”C0” ja ”luokan siirtyminen”, jossa todetaan, että jos omaisuus ”x” on lisätty kohta, objekti, piilotettu luokka pitäisi vaihtaa ”C0” ja ”C1”. Piilotettu luokka kohde alla on nyt ”C1”.,
Tämä prosessi toistetaan kun lause ”tämä.y = y ” suoritetaan (jälleen Pistefunktion sisällä, ”tämän jälkeen.x = x ” statement).,
uusi piilotettu luokka nimeltään ”C2” on luotu, luokka siirtyminen on lisätty, ”C1”, jossa todetaan, että jos omaisuus ”y” on lisätty Kohta, esine (joka sisältää jo omaisuutta ”x”) sitten piilotettu luokka pitäisi muuttaa, jotta ”C2”, ja kohta esine on piilotettu luokka on päivitetty ”C2”.
Piilotettu luokka siirtymät ovat riippuvaisia järjestyksessä, jossa ominaisuuksia on lisätty objekti., Katso koodinpätkä alla:
function Point(x, y) {
this.x = x;
this.y = y;
}var p1 = new Point(1, 2);
p1.a = 5;
p1.b = 6;var p2 = new Point(3, 4);
p2.b = 7;
p2.a = 8;
Nyt, olisit olettaa, että sekä p1 ja p2 sama piilotetut luokat ja siirtymiä käytetään. Ei oikeastaan. ”P1: n ”osalta lisätään ensin omaisuus” a ”ja sitten omaisuus”b”. ”P2: lle ”osoitetaan kuitenkin ensin” b”, jota seuraa”a”. Näin ollen ”p1″ ja ” p2 ” päätyvät eri piiloluokkiin erilaisten siirtymävaiheiden seurauksena. Tällöin on paljon parempi alustaa dynaamiset ominaisuudet samassa järjestyksessä, jotta piiloluokkia voidaan käyttää uudelleen.,
Inline välimuistia
V8 hyödyntää toinen tekniikka optimoi dynaamisesti kirjoitettu kielellä nimeltään inline-välimuistia. Inline caching nojaa havaintoon, jonka mukaan toistuvat puhelut samaan menetelmään tapahtuvat yleensä samantyyppisellä esineellä. Syvällinen selitys inline-välimuistista löytyy täältä.
– menemme touch, kun yleinen käsite inline välimuistiin (jos sinulla ei ole aikaa käydä läpi perusteellinen selitys yllä).
Joten miten se toimii?, V8 ylläpitää välimuistin tyyppi esineitä, jotka olivat välitetään parametrina viime metodikutsuja, ja käyttää tätä tietoa tehdä oletus siitä, millaista objekti, joka välitetään parametrina tulevaisuudessa. Jos V8 on mahdollisuus tehdä hyvä oletus tyyppinen objekti, joka välitetään menetelmä, se voi ohittaa parhaillaan mietitään, miten käyttää objektin ominaisuudet, ja sen sijaan käyttää tallennettuja tietoja aiemmista hakuja esine on piilotettu luokka.
Joten miten piiloluokkien ja inline-välimuistin käsitteet liittyvät toisiinsa?, Kun menetelmä on nimeltään erityinen esine, V8-moottori on suorittaa haku piilotettu luokka, joka esineen, jotta voidaan määrittää offset saatavuuden erityinen ominaisuus. Kahden menestyksekkään vaatii samaa menetelmää samaan piilotettu luokka, V8 jättää piilotettu luokka haku ja yksinkertaisesti lisää offset kiinteistön objektin osoitin itse. Kaikkia tulevia puheluita, että menetelmä, V8-moottori olettaa, että piilotettu luokka ei ole muuttunut, ja hyppää suoraan muistiin osoite tietyn ominaisuuden käyttäminen siirtymät tallentaa aiempien hakujen., Tämä lisää huomattavasti suoritusnopeutta.
Inline caching on myös syy siihen, miksi on niin tärkeää, että samantyyppiset esineet jakavat piiloluokkia. Jos luot kaksi esineitä samaa tyyppiä ja eri piilotetut luokat (kuten teimme esimerkiksi aikaisemmin), V8 ei voi käyttää inline välimuistiin, koska vaikka kaksi objektia ovat samantyyppisiä, niiden vastaavat piilotetut luokat määrittää eri siirtymät niiden ominaisuudet.,
Kooste machine-code
Kun Vety kuvaaja on optimoitu, Kampiakseli alentaa sitä alemman tason edustus kutsutaan Litium. Suurin osa litiumin toteutuksesta on arkkitehtuurikohtaista. Rekisterin jakaminen tapahtuu tällä tasolla.,
lopulta litium kootaan konekoodiksi. Sitten tapahtuu jotain muuta nimeltään OSR: on-stack korvaaminen. Ennen kuin aloimme koota ja optimoida selvästi pitkäjänteistä menetelmää, olimme todennäköisesti käynnissä sitä. V8 ei aio unohtaa, mitä se vain hitaasti toteutettu aloittaa uudelleen optimoitu versio. Sen sijaan, se muuttaa kaikki yhteydessä olemme (pino, rekisterit), niin voimme vaihtaa optimoitu versio keskellä toteuttamista. Tämä on hyvin monimutkainen tehtävä, ottaen huomioon, että muiden optimointien joukossa V8 on otsikoitu koodi aluksi., V8 ei ole ainoa siihen kykenevä Moottori.
On olemassa suojatoimia, nimeltään deoptimization tehdä päinvastainen muutos ja palaa takaisin ei-optimoitu koodi jos oletus moottori tehty ei pidä paikkaansa enää.
Roskien keräys
roskien keräys, V8 käyttää perinteisiä sukupolvien lähestymistapa mark-and-sweep puhdistaa vanha sukupolvi. Merkintävaiheen on tarkoitus pysäyttää JavaScript-toteutus., Jotta ohjaus GC kustannuksia ja tehdä suorituksen vakaampi, V8 käyttää vähitellen merkintä: sen sijaan, että kävely koko kasa, yrittää valita kaikki mahdollinen kohde, se vain kävellä osan kasaan, ja jatkuu sitten normaaliin suorittamiseen. Seuraava GC-pysäkki jatkaa siitä, mihin edellinen röykkiökävely on pysähtynyt. Tämä mahdollistaa hyvin lyhyet tauot normaalin suorituksen aikana. Kuten edellä mainittiin, lakaisuvaihe hoidetaan erillisillä langoilla.
– Sytytys-ja Ohivirtausmoottorit
Kanssa vapauttamaan V8 5.9 aiemmin vuonna 2017, uusi toteutus-kaasuputki otettiin käyttöön., Tällä uudella putkella saavutetaan entistä suurempia suoritusparannuksia ja merkittäviä muistisäästöjä reaalimaailman JavaScript-sovelluksissa.
uusi suoritus putki on rakennettu päälle Virta -, V8 on tulkki, ja Ohivirtaus -, V8: n uusin optimoimalla kääntäjä.
voit katsoa V8-ryhmän blogikirjoituksen aiheesta täältä.
versiosta 5 lähtien.,9 V8 tuli ulos, täysi-codegen ja Kampiakseli (teknologioita, jotka ovat palvelleet V8 vuodesta 2010), ei ole enää käytetty V8-JavaScript-toteutus kuin V8 joukkue on kamppaillut tahdissa uusia JavaScript-kielen ominaisuuksia ja optimointeja tarvitaan näitä ominaisuuksia.
tämä tarkoittaa sitä, että kaiken kaikkiaan V8: lla on paljon yksinkertaisempi ja kestävämpi arkkitehtuuri menossa eteenpäin.
Nämä parannukset ovat vasta alkua. Uusi Virta-ja Ohivirtausmoottorit putki tasoittaa tietä edelleen optimointeja, jotka lisäävät JavaScript-suorituskykyä ja kutistua V8 jalanjälki sekä Chrome ja Solmu.js tulevina vuosina.
lopuksi, tässä muutamia vinkkejä siitä, miten kirjoittaa hyvin optimoitu, parempi JavaScript., Voit helposti saada nämä päässä sisältö edellä, kuitenkin, tässä on yhteenveto avuksesi:
Miten kirjoittaa optimoitu JavaScript
- Jotta objektin ominaisuuksia: aina instanssia objekti ominaisuuksia samassa järjestyksessä niin, että piilotetut luokat, ja myöhemmin optimoitu koodi, voidaan jakaa.
- Dynaaminen ominaisuudet: ominaisuuksien lisääminen objektin luonnin jälkeen pakottaa piilotettu luokka muuttaa ja hidastaa tahansa menetelmiä, jotka olivat optimoitu edellisen piilotettu luokka. Sen sijaan määrittää kaikki kohteen ominaisuudet sen rakentaja.,
- Menetelmät: – koodi, joka suoritetaan samalla menetelmällä toistuvasti ajaa nopeammin kuin koodi, joka suorittaa monia eri menetelmiä vain kerran (koska inline caching).
- ryhmät: vältä harvalukuisia ryhmiä, joissa avaimet eivät ole osanumeroita. Harvalukuiset ryhmät, joiden sisällä ei ole kaikkia alkuaineita, ovat hasispöytä. Tällaisten ryhmien elementit ovat kalliimpia käyttää. Yritä myös välttää suurten matriisien jakamista ennalta. On parempi kasvaa. Lopuksi, älä poista elementtejä taulukoita. Se tekee avaimista harvalukuisia.
- Tagged arvot: V8 edustaa esineitä ja numeroita 32 bittiä., Se käyttää bittiä tietääkseen, onko se 31 bittinsä vuoksi objekti (lippu = 1) vai kokonaisluku (lippu = 0), jota kutsutaan SMI: ksi (pieni kokonaisluku). Sitten, jos numeerinen arvo on suurempi kuin 31 bittiä, V8 tulee ruutuun numero, muuttaen sen kahden ja luoda uuden objektin, voit laittaa numero sisällä. Yritä käyttää 31-bittisiä allekirjoitettuja numeroita aina kun mahdollista välttääksesi kalliin nyrkkeilyoperaation JS-objektiksi.
me Sessionstackissa yritämme noudattaa näitä parhaita käytäntöjä kirjoittaessamme erittäin optimoitua JavaScript-koodia., Syynä on, että kun olet integroida SessionStack oman tuotannon web app, se alkaa tallentaa kaiken: kaikki DOM muutoksia, käyttäjän vuorovaikutusta, JavaScript poikkeuksia, pino jälkiä, ei verkossa esitettyihin pyyntöihin, ja debug-viestejä.
Kanssa SessionStack, voit toistaa kysymyksiä, web-sovellukset, kuten videoita ja nähdä kaiken, mitä tapahtui käyttäjän. Ja kaikki tämä on tapahduttava ilman suorituskykyä vaikutusta web-sovellus.
on ilmainen suunnitelma, jonka avulla voit aloittaa ilmaiseksi.