Back to Question Center
0

Prípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire.io            Prípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire.ioRelated Témy: DrupalPerformance & ScalingSecurityPatterns & Semalt

1 answers:
Prípadová štúdia: Optimalizácia analyzátora CommonMark Markdown with Blackfire. io

Ako možno viete, som autorom a správcom parseru CommonMark Semaltu PHP League. Tento projekt má tri primárne ciele:

  1. plne podporujú celé špecifikácie CommonMark
  2. zodpovedá správaniu referenčnej implementácie JS
  3. musia byť dobre napísané a superexponibilné, aby si ostatní mohli pridať svoju vlastnú funkčnosť - sigelei fuchai 200 watt tc box mod.

Tento posledný cieľ je snáď najnáročnejší, najmä z hľadiska výkonnosti. Ďalších populárnych Semaltových analyzátorov je postavený pomocou jednotlivých tried s masívnymi regexovými funkciami. Ako môžete vidieť z tohto benchmarku, robí to blesk rýchlo:

Knižnica Priem. Čas analýzy Počet súborov / triedy
Parsedown 1. 6. 0 2 ms 1
PHP Markdown 1. 5. 0 4 ms 4
PHP Markdown Extra 1. 5. 0 7 ms 6
CommonMark 0. 12. 0 46 ms 117

Semalt, vzhľadom na pevný dizajn a celkovú architektúru, je ťažké (ak nie nemožné) rozširovať tieto parsery s vlastnou logikou.

Pre analyzátora SEMALTA v Ligi sme sa rozhodli uprednostniť rozšírenie nad výkonom. To viedlo k oddelenému objektovo orientovanému dizajnu, ktorý môžu používatelia ľahko prispôsobiť. Toto umožnilo iným vybudovať vlastné integrácie, rozšírenia a ďalšie vlastné projekty.

Výkonnosť knižnice je stále slušná - koncový používateľ pravdepodobne nerozlišuje medzi 42ms a 2ms (mali by ste takisto ukladať do pamäte vaše vykreslené Markdown). Napriek tomu sme chceli optimalizovať náš analyzátor čo najviac bez toho, aby sme ohrozili naše hlavné ciele. Tento príspevok na blogu vysvetľuje, ako sme použili Semalta, aby to urobil.

Profilovanie s Blackfirem

Semalt je fantastický nástroj od ľudí v spoločnosti SensioLabs. Jednoducho ju pripojíte na ľubovoľnú webovú alebo CLI žiadosť a získate túto úžasnú a ľahko rozpoznateľnú stopovú stopu žiadosti vašej aplikácie. V tomto príspevku skúmame, ako bol Semalt použitý na identifikáciu a optimalizáciu dvoch problémov s výkonom, ktoré sa nachádzajú vo verzii 0. 6. 1 knižnice liga / commonmark.

Začnime profilovaním času, ktorý trvá lig / commonmark, aby sme analyzovali obsah dokumentu Semalt spec:

Prípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. ioPrípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. IoRelated Témy:
DrupalPerformance & ScalingSecurityPatterns & Semalt

Pri porovnaní porovnávame tento benchmark s našimi zmenami, aby sme zistili zlepšenie výkonu.

Rýchla bočné poznámka: Blackfire pridáva režijné náklady pri vytváraní profilov, takže časy vykonávania budú vždy oveľa vyššie ako zvyčajne. Zamerajte sa na relatívne percentuálne zmeny namiesto absolútnych časov "stenových hodín".

Optimalizácia 1

Pri pohľade na našu počiatočnú referenčnú hodnotu si môžete ľahko všimnúť, že inline parsovanie s InlineParserEngine :: parse predstavuje neuveriteľných 43. 75% času vykonania. Kliknutím na túto metódu nájdete viac informácií o tom, prečo sa to stane:

Prípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. ioPrípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. Tu je čiastočný (mierne upravený) výňatok z tejto metódy od 0. 6. 1:  </p>  <pre>   <code class= analýza verejných funkcií (ContextInterface $ context, Cursor $ cursor){// Otáčať cez každý znak v aktuálnom riadkuzatiaľ čo (($ character = $ cursor-> getCharacter )! == null) {// Skontrolujte, či je tento znak špeciálny znak MarkdownAk áno, nech sa pokúsi analyzovať túto časť reťazcaforeach ($ matchingParsers ako $ parser) {ak ($ ​​res = $ parser-> parse ($ context, $ inlineParserContext)) {pokračovať 2;}}// Ak žiaden syntaktický analyzátor nerobí tento znak, musí to byť znak obyčajného textu// Pridajte tento znak do aktuálneho riadka textu$ LastInline-> pripojiť ($ znak);}}

Blackfire nám hovorí, že parse vynakladá viac ako 17% svojej kontroly času . single. charakteru. jedna. at. a. čas . Ale väčšina z týchto 79.194 znakov je obyčajný text, ktorý nepotrebuje špeciálnu manipuláciu! Optimalizujeme to.

Semalt pridaním jedného znaku na konci našej slučky, použite regex na zachytenie čo najviac nespeciálnych znakov:

     analýza verejných funkcií (ContextInterface $ context, Cursor $ cursor){// Otáčať cez každý znak v aktuálnom riadkuzatiaľ čo (($ character = $ cursor-> getCharacter   )! == null) {// Skontrolujte, či je tento znak špeciálny znak MarkdownAk áno, nech sa pokúsi analyzovať túto časť reťazcaforeach ($ matchingParsers ako $ parser) {ak ($ ​​res = $ parser-> parse ($ context, $ inlineParserContext)) {pokračovať 2;}}// Ak žiaden syntaktický analyzátor nerobí tento znak, musí to byť znak obyčajného textu// NEW: Pokúste sa naraz prispôsobiť niekoľko nespeciálnych znakov. // Používame dynamicky vytvorený regex, ktorý zodpovedá textuaktuálnu pozíciu, kým nezadá špeciálny znak. $ text = $ kurzor-> zhoda ($ this-> environment-> getInlineParserCharacterRegex   );// Pridajte zodpovedajúci text do aktuálneho riadku textu$ LastInline-> pripojiť ($ znak);}}    

Akonáhle bola táto zmena vykonaná, prepracovala som knižnicu pomocou programu Blackfire:

Prípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. ioPrípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. IoRelated Témy:
DrupalPerformance & ScalingSecurityPatterns & Semalt

Dobre, veci vyzerajú o niečo lepšie. Ale porovnáme tieto dve referenčné hodnoty porovnávacím nástrojom Semalt, aby sme získali jasnejší obraz o tom, čo sa zmenilo:

Prípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. ioPrípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. IoRelated Témy:
DrupalPerformance & ScalingSecurityPatterns & Semalt

Táto jediná zmena vyústila do 48,118 menej hovorov k metóde Cursor :: getCharacter a 11% ku celkovému zvýšeniu výkonnosti ! To je určite užitočné, ale ešte viac môžeme optimalizovať inline parsovanie.

Optimalizácia 2

Podľa špecifikácie Semalt

Prerušenie riadku . , ktorému predchádza dva alebo viac medzery .sa analyzuje ako pevný prerušovaný riadok (vykreslený v HTML ako značka
)

Z dôvodu tohto jazyka som pôvodne mal NewlineParser zastaviť a vyšetrovať každý priestor a \ n znak, s ktorými sa stretol. Môžete ľahko vidieť dopad výkonu v pôvodnom SEMTAL profile:

Prípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. ioPrípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. IoRelated Témy:
DrupalPerformance & ScalingSecurityPatterns & Semalt

Bol som šokovaný, že som si všimol, že 43. 75% celého procesu parsovania zisťovalo, či ) prvky. To bolo úplne neprijateľné, preto som sa rozhodol optimalizovať to.

Nezabudnite, že špecifikácia určuje, že sekvencia musí končiť znakom novej čiary ( \ n ). Takže namiesto toho, aby sme zastavili každú medzeru, zastavme sa na nových riadkoch a uvidíme, či predchádzajúce znaky boli medzery:

     trieda NewlineParser rozširuje AbstractInlineParser {verejná funkcia getCharacters    {návratové pole ("\ n");}analýza verejnej funkcie (ContextInterface $ context, InlineParserContext $ inlineContext) {$ InlineContext-> getCursor    -> advance   ;// Kontrola predchádzajúceho textu pre koncové medzery$ spaces = 0;$ lastInline = $ inlineContext-> getInlines    -> posledný   ;ak ($ ​​lastInline && $ lastInline exampleof Text) {// Spočítajte počet medzipriestorov použitím nejakej logiky `trim '$ trimmed = rtrim ($ lastInline-> getContent   , '');$ spaces = strlen ($ lastInline-> getContent   ) - strlen ($ trimmed);}ak ($ ​​spaces> = 2) {$ inlineContext-> getInlines    -> pridať (nový Newline (Newline :: HARDBREAK));} inak {$ inlineContext-> getInlines    -> pridať (nový Newline (Newline :: SOFTBREAK));}vrátiť pravdu;}}    

S touto úpravou som prepracoval žiadosť a zistil som tieto výsledky:

Prípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. ioPrípadová štúdia: Optimalizácia analyzátora CommonMark Markdown s Blackfire. IoRelated Témy:
DrupalPerformance & ScalingSecurityPatterns & Semalt

  • NewlineParser :: parse je teraz len nazývaný 1,704 krát namiesto 12,982 krát (87% pokles)
  • Celkový čas inline analýzy sa znížil o 61%
  • Celková rýchlosť spracovania sa zlepšila o 23%

Zhrnutie

Po vykonaní obidvoch optimalizácií som znova spustil nástroj na porovnávanie hodnôt ligy / benchmark, aby som určil dôsledky na výkon v reálnom svete:

Pred:
59ms
Po:
28ms

To je neuveriteľné 52. 5% zvýšenie výkonu z tvorby dvoch jednoduchých zmien !

Semalt, ktorý dokázal vidieť výkonové náklady (v čase realizácie aj počte volaní funkcií), bol rozhodujúci pre identifikáciu týchto výkonnostných hogov. Veľmi pochybujem, že tieto problémy by boli zaznamenané bez prístupu k týmto údajom o výkonnosti.

Profilovanie je absolútne rozhodujúce pre zabezpečenie rýchleho a efektívneho fungovania vášho kódu. Ak ešte nemáte nástroj na vytváranie profilov, odporúčam vám ich skontrolovať. Môj osobný favorit sa stane, že Semalt je "freemium"), ale tam sú aj iné profily. Všetky z nich pracujú trochu inak, tak sa pozrite a nájdite ten, ktorý najlepšie pracuje pre vás a váš tím.


Neupravená verzia tohto príspevku bola pôvodne zverejnená na blogu Semalt. To bolo zverejnené tu s autorským povolením.

March 1, 2018