Teljesítmény

Profilozás

A profilozás a program végrehajtásának elemzéséről és az összesített adatok méréséről szól. Ezek az adatok lehetnek az egyes függvények eltelt ideje, a végrehajtott SQL lekérdezések…

Bár a profilozás önmagában nem javítja a program teljesítményét, nagyon hasznos lehet a teljesítményproblémák felderítésében és annak azonosításában, hogy a program mely része felelős ezekért.

Az Odoo egy integrált profilozó eszközt biztosít, amely lehetővé teszi az összes végrehajtott lekérdezés és veremnyomkövetés rögzítését a végrehajtás során. Használható egy felhasználói munkamenet kéréseinek készletének vagy egy adott kódrészlet profilozására. A profilozási eredmények megtekinthetők az integrált speedscope nyílt forráskódú alkalmazás, amely lehetővé teszi a lánggrafikon megjelenítését nézetben, vagy egyedi eszközökkel elemezhetők, ha először JSON fájlba vagy az adatbázisba mentjük őket.

A profilozó engedélyezése

A profilozó engedélyezhető a felhasználói felületről, ami a legegyszerűbb módja, de csak webes kérések profilozását teszi lehetővé, vagy Python kódból, ami lehetővé teszi bármely kódrészlet, beleértve a teszteket is, profilozását.

  1. A fejlesztői mód engedélyezése.

  2. Mielőtt egy profilozási munkamenetet elindítanánk, a profilozót globálisan engedélyezni kell az adatbázison. Ez kétféleképpen történhet:

    • Open the developer mode tools, then toggle the Enable profiling button. A wizard suggests a set of expiry times for the profiling. Click on ENABLE PROFILING to enable the profiler globally.

      ../../../_images/enable_profiling_wizard.png
    • Menjen a Beállítások –> Általános beállítások –> Teljesítmény menüpontra, és állítsa be a kívánt időt a Profilozás engedélyezése eddig mezőben.

  3. After the profiler is enabled on the database, users can enable it on their session. To do so, toggle the Enable profiling button in the developer mode tools again. By default, the recommended options Record sql and Record traces are enabled. To learn more about the different options, head over to Gyűjtők.

    ../../../_images/profiling_debug_menu.png

Amikor a profilozó engedélyezve van, az összes szerverhez intézett kérés profilozásra kerül és elmentésre kerül egy ir.profile rekordba. Ezek a rekordok a jelenlegi profilozási munkamenetbe vannak csoportosítva, amely a profilozó engedélyezésétől annak letiltásáig tart.

Megjegyzés

Az Odoo Online adatbázisok nem profilozhatók.

Az eredmények elemzése

To browse the profiling results, make sure that the profiler is enabled globally on the database, then open the developer mode tools and click on the button in the top-right corner of the profiling section. A list view of the ir.profile records grouped by profiling session opens.

../../../_images/profiling_web.png

Minden rekordnak van egy kattintható linkje, amely új lapon nyitja meg a speedscope eredményeket.

../../../_images/flamegraph_example.png

A Speedscope nem tartozik ennek a dokumentációnak a hatáskörébe, de sok eszköz áll rendelkezésre kipróbálásra: keresés, hasonló keretek kiemelése, nagyítás a kereten, idővonal, bal oldali nehéz, szendvics nézet…

A bekapcsolt profilozási opcióktól függően az Odoo különböző nézetmódokat generál, amelyekhez a felső menüből férhet hozzá.

../../../_images/speedscope_modes.png
  • A Kombinált nézet az összes SQL lekérdezést és nyomkövetést egyesítve mutatja.

  • A Kombinált kontextus nélkül nézet ugyanazt az eredményt mutatja, de figyelmen kívül hagyja a mentett végrehajtási kontextust <performance/profiling/enable>`.

  • A sql (nincs rés) nézet az összes SQL lekérdezést úgy mutatja, mintha egymás után hajtották volna végre, bármilyen Python logika nélkül. Ez hasznos lehet csak az SQL optimalizálásához.

  • A sql (sűrűség) nézet csak az összes SQL lekérdezést mutatja, közöttük rést hagyva. Ez hasznos lehet annak megállapítására, hogy az SQL vagy a Python kód a probléma, és azonosítani azokat a zónákat, ahol sok kis lekérdezést lehetne csoportosítani.

  • A keretek nézet csak a periodikus gyűjtő eredményeit mutatja.

Fontos

Bár a profilozót úgy tervezték, hogy a lehető legkönnyebb legyen, még mindig hatással lehet a teljesítményre, különösen a Sync gyűjtő használatakor. Ezt tartsa szem előtt a speedscope eredmények elemzésekor.

Gyűjtők

Míg a profilozó a profilozás mikor-járól szól, addig a gyűjtők a mit-ről gondoskodnak.

Each collector specializes in collecting profiling data in its own format and manner. They can be individually enabled from the user interface through their dedicated toggle button in the developer mode tools, or from Python code through their key or class.

Jelenleg négy gyűjtő érhető el az Odoo-ban:

Név

Kapcsoló gomb

Python kulcs

Python osztály

SQL gyűjtő

Rekord sql

sql

SqlCollector

Időszakos gyűjtő

Nyomkövetések rögzítése

traces_async

PeriodicCollector

QWeb gyűjtő

Qweb rögzítése

qweb

QwebCollector

Szinkron gyűjtő

Nem

traces_sync

SyncCollector

Alapértelmezés szerint a profilozó engedélyezi az SQL és a Periodikus gyűjtőket. Mind a felhasználói felületről, mind a Python kódból történő engedélyezés esetén.

SQL gyűjtő

Az SQL gyűjtő elmenti az adatbázisba a jelenlegi szálon végrehajtott összes SQL lekérdezést (minden kurzorra), valamint a verem nyomvonalát. A gyűjtő többletterhelése hozzáadódik az elemzett szálhoz minden lekérdezésnél, ami azt jelenti, hogy sok kis lekérdezés használata befolyásolhatja a végrehajtási időt és más profilozókat.

Különösen hasznos a lekérdezésszámok hibakeresésére, vagy információk hozzáadására a Periodikus gyűjtő kombinált speedscope nézetben.

Periodikus gyűjtő

Ez a gyűjtő külön szálon fut, és elmenti az elemzett szál verem nyomvonalát minden intervallumban. Az intervallum (alapértelmezés szerint 10 ms) meghatározható a felhasználói felületen az Intervallum opcióval, vagy a interval paraméterrel a Python kódban.

Figyelem

Ha az intervallum nagyon alacsony értékre van állítva, a hosszú kérések profilozása memória problémákat okozhat. Ha az intervallum nagyon magas értékre van állítva, az információk a rövid függvényvégrehajtásokról elvesznek.

Ez az egyik legjobb módja a teljesítmény elemzésének, mivel külön szálának köszönhetően nagyon alacsony hatással van a végrehajtási időre.

QWeb gyűjtő

Ez a gyűjtő elmenti az összes direktíva Python végrehajtási idejét és lekérdezéseit. Ahogy az SQL gyűjtő, a többletterhelés jelentős lehet sok kis direktíva végrehajtásakor. Az eredmények eltérnek más gyűjtőktől az összegyűjtött adatok tekintetében, és az ir.profile űrlap nézetből egyedi widget segítségével elemezhetők.

Elsősorban nézetek optimalizálására hasznos.

Szinkron gyűjtő

Ez a gyűjtő elmenti a verem minden függvényhívás és visszatérés esetén, és ugyanazon a szálon fut, ami jelentősen befolyásolja a teljesítményt.

Hasznos lehet a bonyolult folyamatok hibakeresésére és megértésére, valamint azok végrehajtásának követésére a kódban. Azonban nem ajánlott teljesítmény elemzésére, mert a többletterhelés magas.

Teljesítménybeli buktatók

  • Legyen óvatos a véletlenszerűséggel. Többszöri végrehajtás különböző eredményekhez vezethet. Például, ha a szemétgyűjtő aktiválódik a végrehajtás során.

  • Legyen óvatos a blokkoló hívásokkal. Bizonyos esetekben a külső c_call időt vehet igénybe, mielőtt elengedi a GIL-t, ami váratlanul hosszú keretekhez vezethet a Időszakos gyűjtő esetén. Ezt a profilozónak észlelnie kell, és figyelmeztetést kell adnia. Lehetőség van a profilozó manuális aktiválására az ilyen hívások előtt, ha szükséges.

  • Figyeljen a gyorsítótárra. A profilozás előtt, hogy a view/assets/… a gyorsítótárban van, különböző eredményekhez vezethet.

  • Legyen tisztában a profilozó többletterhelésével. A SQL gyűjtő többletterhelése jelentős lehet, amikor sok kis lekérdezés kerül végrehajtásra. A profilozás hasznos lehet egy probléma észlelésére, de előfordulhat, hogy le szeretné tiltani a profilozót, hogy mérje a kódváltoztatás valódi hatását.

  • A profilozási eredmények memóriaigényesek lehetnek. Bizonyos esetekben (pl. egy telepítés vagy hosszú kérés profilozása) előfordulhat, hogy eléri a memóriahatárt, különösen a speedscope eredmények megjelenítésekor, ami HTTP 500 hibához vezethet. Ebben az esetben előfordulhat, hogy a szervert magasabb memóriahatárral kell indítania: --limit-memory-hard $((8*1024**3)).

Jó gyakorlatok

Tömeges műveletek

Amikor rekordhalmazokkal dolgozik, szinte mindig jobb a tömeges műveletek alkalmazása.

Example

Ne hívjon meg olyan metódust, amely SQL lekérdezéseket futtat, miközben egy rekordhalmazon iterál, mert ezt minden egyes rekordnál megteszi.

def _compute_count(self):
    for record in self:
        domain = [('related_id', '=', record.id)]
        record.count = other_model.search_count(domain)

Ehelyett cserélje le a search_count-ot egy _read_group-ra, hogy egyetlen SQL lekérdezést hajtson végre az egész rekordhalmazra.

def _compute_count(self):
    domain = [('related_id', 'in', self.ids)]
    counts_data = other_model._read_group(domain, ['related_id'], ['__count'])
    mapped_data = dict(counts_data)
    for record in self:
        record.count = mapped_data.get(record, 0)

Megjegyzés

Ez a példa nem optimális és nem helyes minden esetben. Csak a search_count helyettesítésére szolgál. Egy másik megoldás lehet az előzetes betöltés és az inverz One2many mező számlálása.

Example

Ne hozzon létre rekordokat egymás után.

for name in ['foo', 'bar']:
    model.create({'name': name})

Ehelyett halmozza fel a létrehozási értékeket, és hívja meg a create metódust a tömegre. Ennek többnyire nincs hatása, és segíti a keretrendszert a mezők számításának optimalizálásában.

create_values = []
for name in ['foo', 'bar']:
    create_values.append({'name': name})
records = model.create(create_values)

Example

Nem sikerült előtölteni a rekordhalmaz mezőit, miközben egyetlen rekordot böngészünk egy ciklusban.

for record_id in record_ids:
    model.browse(record_id)
    record.foo  # One query is executed per record.

Ehelyett először böngéssze át az egész rekordhalmazt.

records = model.browse(record_ids)
for record in records:
    record.foo  # One query is executed for the entire recordset.

Ellenőrizhetjük, hogy a rekordok előtöltése csoportosan történik-e, ha elolvassuk a prefetch_ids mezőt, amely tartalmazza az egyes rekordazonosítókat. az összes rekord együttes böngészése nem praktikus,

Szükség esetén a with_prefetch metódus használható a csoportos előtöltés letiltására:

for values in values_list:
    message = self.browse(values['id']).with_prefetch(self.ids)

Csökkentse az algoritmikus komplexitást

Az algoritmikus komplexitás annak mértéke, hogy egy algoritmus mennyi idő alatt fejeződik be a bemenet n méretéhez viszonyítva. Ha a komplexitás magas, a végrehajtási idő gyorsan növekedhet, ahogy a bemenet nagyobb lesz. Bizonyos esetekben az algoritmikus komplexitás csökkenthető a bemenet adatok helyes előkészítésével.

Example

Egy adott probléma esetén vegyünk figyelembe egy naiv algoritmust, amelyet két beágyazott ciklussal készítettek, és amelynek komplexitása O(n²).

for record in self:
    for result in results:
        if results['id'] == record.id:
            record.foo = results['foo']
            break

Feltételezve, hogy minden eredménynek különböző azonosítója van, előkészíthetjük az adatokat a komplexitás csökkentése érdekében.

mapped_result = {result['id']: result['foo'] for result in results}
for record in self:
    record.foo = mapped_result.get(record.id)

Example

A rossz adatstruktúra kiválasztása a bemenet tárolására kvadratikus komplexitáshoz vezethet.

invalid_ids = self.search(domain).ids
for record in self:
    if record.id in invalid_ids:
        ...

Ha az invalid_ids egy lista-szerű adatstruktúra, az algoritmus komplexitása kvadratikus lehet.

Ehelyett inkább használjon halmazműveleteket, például az invalid_ids halmazzá alakítását.

invalid_ids = set(invalid_ids)
for record in self:
    if record.id in invalid_ids:
        ...

A bemenettől függően rekordhalmaz műveletek is használhatók.

invalid_ids = self.search(domain)
for record in self - invalid_ids:
    ...

Használjon indexeket

Az adatbázis indexek segíthetnek a keresési műveletek gyorsításában, legyen az keresés az vagy a felhasználói felületen keresztül.

name = fields.Char(string="Name", index=True)

Figyelem

Ügyeljen arra, hogy ne indexeljen minden mezőt, mivel az indexek helyet foglalnak és hatással vannak a teljesítményre az INSERT, UPDATE és DELETE végrehajtásakor.