Performance tunning — jak na to?

Petr Jůza
OpenWise Tech blog
Published in
7 min readDec 2, 2020

S laděním performance (performance tunning, performance optimization) se potkal asi každý v průběhu své IT pracovní činnosti, někdy nevědomě, kdy se jednalo o drobná vylepšení v kódu vedoucí ke zrychlení konkrétní funkčnosti, jindy vědomě, kdy se na zlepšení performance vytvořil samostatný projekt.

Stejně jako jiné technické činnosti, tak i performance tunning lze dělat více dobře nebo více špatně. Já se v tomto článku pokusím shrnout mé zkušenosti z této oblasti, poukázat na nejčastější chyby nebo naopak říci co může fungovat. Ladění performance se v nahodilých intervalech vlastně věnuji celou mojí pracovní kariéru, dokonce určitý krátký čas jsem se tomuto tématu věnoval výhradně.

Předpoklady pro úspěšný tunning

Hlavní předpoklad je v zásadě pouze jeden — znát přesně požadavky na systém, znát cílovou metu.

Ladění performance lze označit za systematickou činnost, při které postupně směřujeme k cílovým požadavkům, které pokud nebudou zcela exaktně definované, tak nemůžeme vědět, kam vlastně směřujeme. Jednak si často musíme umět vybrat, co je pro nás důležitější, jaké performance parametry mají přednost — chceme systém s co nejnižší latencí nebo naopak s co největší prostupností (throughput)? Je pro nás nezbytností datová konzistence (consistency) a trvanlivost (durability) nebo můžeme obětovat tyto vlastnosti pro dostupnost (availability)? Nemohu chtít vždy všechno (typicky volba dvou ze tří kritérií v CAP teorému) a musím co nejpřesněji znát co je mojí prioritou, protože mě to pak výrazně usnadní v rozhodováních, které budu v průběhu performance ladění muset dělat.

Cílové parametry je nutné mít zcela přesně definované, nic ve smyslu jako “o něco rychlejší” nebo “stejně rychlé jako tato funkčnost” neobstojí. Jak uvidíte dále, vše musí být zcela jasně měřitelné, protože co se nedá měřit, to nelze vylepšovat (nebo aspoň nikoliv efektivně). Tedy správná definice je např. “po dobu 30 minut je očekávaná prostupnost 1.000 requestů/s, maximální latence 500ms”.

Zkušenost je taková, že se tyto parametry často definují až v momentě, kdy je nutné řešit nějaký problém, přitom před implementací jakékoliv nové funkčnosti by se měli definovat nejen funkční požadavky, ale i ty nefunkční a tím nastavit akceptační kritéria nejen z pohledu uživatele, ale také z pohledu provozování. Neumluvě o tom, že podle nefunkčních požadavků se dělá sizing HW.

Aby člověk mohl efektivně řešit technické otázky, musí dle mého názoru znát i businessový kontext, protože až potom bude schopen vše řešit s dostatečným nadhledem. Těžko se ladí performance, když člověk neví jaká data tam putují, co je jejich obsahem, jaká specifika mají apod. Často to jsou právě různé businessově okrajové požadavky, na které se během vývoje nebo testování trochu pozapomělo a nyní se to na produkci vyboulilo.

Měřit, měřit, měřit …

Nejednou si vzpomenu na mého vysokoškolského učitele, který nám opakovaně vysvětloval, že Teorie řízení je exaktní technický obor a vše lze naprosto přesně změřit a zaznamenat. A díky tomu lze systematickým přístupem věci zlepšovat, ale vždy za podmínky, že přesně vím kde jsem, tedy umím měřit, opakovaně měřit, opakovaně měřit za stejných podmínek.

To se celkem jednoduše řekne, ale zajistit konzistentní a stabilní prostředí není vždy úplně jednoduché, stačí aby těch systémů nebo komponent po cestě bylo více a hned se náročnost tohoto úkolu zvyšuje. Umět měřit co přesně potřebuji také není tak jednoduché jak se na první pohled zdá, protože takové metriky budu muset třeba doprogramovat, budu muset zavést systém pro sběr takových dat, abych je uměl následně analyzovat a dávat do souvislostí. Zde hrají nenahraditelnou roli systémy jako Elasticsearch, Kibana, Grafana, Prometheus, ZipKin apod.

Organizace a tým

Máme referenční prostředí, víme co je naším cílem a umíme měřit aktuální stav — můžeme se tedy do toho pustit … Možná se ještě na chvíli zastavme u organizace celé aktivity, jaké role vlastně potřebujeme k efektivnímu řešení.

Hlavním hybatelem je vetšinou člověk s rolí solution nebo aplikačního architekta (v případě, že řešíme performance v rámci jedné aplikace resp. komponenty), jehož hlavním cílem je zacílit, tedy ukázat na možná slabá místa, ukázat na konkrétní komponenty k dalšímu podrobnějšímu řešení. Tento, hlavní, člověk je zodpovědný za správné směřování celé aktivity, koordinuje dílčí akce a udržuje informaci, kde se právě tým nachází a co bude nutné dále řešit. U něj se potkávají všechny důležité informace, on je primárně vyhodnocuje. Tento člověk je rolí převážně architekt, částečně organizátor a čím více znalec technologií jednotlivých komponent, tím určitě lépe.

Pokud performance ladíme přes více komponent, tak se neobejdeme bez detailních znalostí pro každou takovou komponentu, pokud na ní ukážeme, že je potenciálně problematická. Ať už se jedná o Java nebo .Net aplikaci nebo databázi nebo cache nebo různé infrastrukturní komponenty, vždy bude potřeba někdo, kdo konkrétní pochybnost prozkoumá a vysvětlí, později případně opraví. Toto může být vývojář, externí expert za danou technologii, prostě kdokoliv, kdo dané komponentě opravdu rozumí.

A samozřejmě potřebujeme mít k dispozici testing (nejčastěji toto řeší právě on, může být ale i tým provozu nebo samotný vývojový tým, záleží co se a kde se přesně ladí), který bude schopný opakovaně provádět performance testy a měřit jejich výsledek. Je potřeba umět testovat jak jednotlivé komponenty izolovaně, tak i celek dohromady — toto platí zejména pro různé komunikační komponenty (např. Apache Kafka), kdy nejednou jsem byl svědkem toho, že jednotlivé komponenty izolovaně fungovali zcela bez problémů, nicméně dohromady již nikoliv.

Nejčastější chyby

V poslední části bych uvedl několik častých chyb, na které jsem měl možnost narazit

  • je potřeba předcházet špatným rozhodnutím, bez dobrého návrhu architektury to nepůjde. Přijde mě, že se často více řeší lokální performance, aby daný algoritmus fungoval co nejlépe, nicméně je to celkový design, který rozhoduje o tom, že to celé bude fungovat jak má, že to bude performovat. Problémy na úrovni algoritmu se fixují celkem jednoduše, iterativním způsobem se to většinou dají vyřešit, ale chyby v návrhu celého řešení se už jednoduše neodstraní. A jak určitě sami víte, tak opravy chyb v návrhu jsou ty nejdražší. Je proto důležité dělat i důkladné review architektury, nejen kódu.
  • problémy nemusí být jen v samotných aplikacích, ale i v infrastruktuře samotné. Nejednou jsme zjistili síťové nebo serverové omezení, např. prostupnost routeru omezovala prostupnost pro synchronizaci dat v rámci replik v REDIS clusteru nebo síťové disky zapisovaly data pomaleji než se čakalo z důvodu nastavení vysoké úrovně datové komprese a konzistence. Tedy doporučení je takové, že je potřeba v analýzách počítat úplně se vším co daný request po cestě potká.
  • nic není jasné, vše se musí nejdřívě změřit a potvrdit. Často při performance ladění slyším, že “toto není nutné řešit, tam určitě problém nebude”. Ano, možná nebude, ale zde je lepší nikomu nevěřit a vše si měřením ověřit. Zde může do jisté míry i pomoci zapojení externistů, protože ti jednak nebudou zatíženi “znalostmi místního prostředí a poměrů” a budou tedy k problému přistupovat více s otevřenou hlavou bez jakýchkoliv předsudků
  • referenční prostředí musí být co nejbliže produkčnímu. Toto se jednoduše řekne, ale často už hůře realizuje. Nicméně je celkem zásadní, že pokud mě trápí performance v produkci, tak musím mít takové prostředí na testování a ladění, které té produkci bude nejvíce odpovídat, jinak řeším jiný problém než je ten původní. Mohu si pomoci tím, že na základě faktických a měřitelných informací se zaměřím pouze na vybrané komponenty a tím nebudu muset mít k dispozici celé prostředí.
  • hledání zkratek nefunguje. Nejednou se nám stalo, že jsme dali více na naše pocity než na systematický přístup, přeskočili několik kroků a začali řešit to, kde jsme si mysleli, že je problém. Bohužel často se ukázalo, že jsme neměli pravdu a místo urychlení nám to přineslo chaos a nejistotu do celé aktivity.
  • je nutné být schopen odolat tlakům managementu. Tento bod souvisí s předchozím bodem, protože nejednou jsem byl svědkem výrazného tlaku od vedení na co nejrychlejší vyřešení problému. Jejich postoj chápu, nicméně oni musí zase pochopit, že příčina aktuálních problémů leží někde v minulosti, a že není možné tyto věci zjistit, natož změnit hned do druhého dne. Technologický dluh je všude a není cílem ho mít vždy nulový, ale je potřeba vhodně balancovat mezi riziky takového dluhu a náklady spojenými s jeho snižováním.
  • jako každé jiné testování, tak i performance testing hledá chyby a upozorňuje na ně. Je to mix komunikačních schopností, empatie a důvěryhodnosti jak tyto věci komunikovat dále do týmů k opravě, jak takové týmy umět do aktivity zapojit a získat je na svoji stranu. Opět zde může být výhodou zapojení někoho externího.

Závěr

Zažil jsem ladění peformance, které trvalo měsíce, ale i akce, kdy jsme během prvních pokusů byli schopni odhalit příčinu a během druhého dne jsme ji opravili a vše fungovalo jak mělo. Z mých zkušeností je ale blíže k realitě první scénář, kdy je nutné nastavit systematický přístup k řešení a krok po kroku pokračovat směrem do cíle. Někdy to může trvat dny, jindy týdny nebo měsíce, ale pokud vše máme správně nastavené a postupujeme systematicky, pak jdeme správným směrem. Bohužel zkratky málokdy fungují, a proto je potřeba být trpělivý a vytrvalý, vědět v každém okamžiku kde přesně jsem, proč to dělám a jaké budou další kroky. K tomu potřebuji mít připravený monitoring s vhodnými metrikami.

PS: pár konkrétních fuckupů na konec

  • Spring SpEL obsahoval nevhodně zapsaný výraz, který se nemohl optimalizovat a navíc se vždy řešil pomocí rekurze.
  • podobně při použití Apache Kafky a Avra, kde se někde používal GenericRecord, který k získání hodnot využívá reflexi.
  • zcela špatně použitý pattern a to řešení load balancingu pomocí Kafky
  • podobně použití queue patternu v CassandraDB
  • v jedné nejmenované české bance se u internetového bankovnictví z ničeho nic při ranní špičce zvýšila latence asi 10x a response time se počítal v řádu vyšších jednotek sekund. Pár dní předem byl release. Nikdo nevědel co se stalo, procházel se nově přidaný kód, připravovalo se prostředí pro perf testy až si někdo všiml, že velikost Javascriptových knihoven nemá původních pár stovek kB, ale necelé dva MB a to z toho důvodu, že se povýšíla verze jedné z knihoven, která si dotáhnula další knihovny atd. Aby toho nebylo málo, tak na homepage v místě pro kontextovou reklamu se začal objevovat banner o velikosti 1.5 MB, protože někdo zapomněl udělat optimalizaci obrázku.
  • říká se, že REDIS nikdy nemůže být pomalý, ale přesto to tak vypadalo. Jak to tak bývá, věcí k optimalizaci jsme našli více, samotný REDIS v tom byl nevinně — nefunkční connection pooling mezi aplikací a REDISem, příliš velká data u několika klíčů, které se často měnily a četly a tím se “zacpaly” I/O buffery, nevhodné používání keys a nebo úzké infrastrukturní hrdlo v podobě routeru, přes který probíhala replikace dat na slave instance

--

--