wiki:Documentatie/Ontwikkelaar/Omgeving/Performance

TOC(heading=Performance meten)? TOC(heading=Omgeving, sectionindex, compact, depth=3, allactive, Documentatie/Ontwikkelaar/Omgeving/)? TOC(heading=Ontwikkelaar, sectionindex, compact, depth=2, allactive, Documentatie/Ontwikkelaar/)? TOC(heading=Documentatie, sectionindex, compact, depth=1, allactive, Documentatie/)?

Performance meten

(ook iets als "kwantitatieve software evaluatie")

Om metingen op OpenAC zelf uit te kunnen voeren om zo een kwantitatief beeld te krijgen van de software, hebben we tools nodig om getallen te produceren op basis van de software. Deze pagina beschrijft sommige beschikbare en gebruikte tools.

Geheugengebruik

Het meest nauwkeurig voor geheugenmeting in Python onder Linux is heapy, een tool om (interactief) de Python heap te onderzoeken. De handleiding voor heapy is nogal summier, maar er zit veel in de help binnen de module zelf. Met heapy kan je kijken hoeveel geheugen er gebruikt wordt, wat voor types het heeft, en ook zien welke veranderingen in het geheugen gebeuren als je bepaalde functionaliteit aanroept.

Installatie van Heapy

Instructies voor OpenSUSE

  • zypper in python-devel
  • Je moet guppy waarschijnlijk van source compileren:
    $ svn co https://guppy-pe.svn.sourceforge.net/svnroot/guppy-pe/trunk/guppy
    $ cd guppy
    $ python setup.py build 
    $ sudo python setup.py install 
    

Vervolgens kan je de (nogal basic) heapy tutorial volgen.

Voorbeeldsessie Heapy

>>> from guppy import hpy; heap=hpy(); del hpy
>>> heap.heap()
Partition of a set of 26126 objects. Total size = 1889564 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  11935  46   728612  39    728612  39 str

Op dit moment -- met alleen Python gestart -- is er 1.8MB in gebruik, vooral strings.

>>> heap.setref()
>>> import logica
[2012-03-17 01:04:19]:WARNING:openac:Functie doorvoer is deprecated bij callbacks maar zit nog in ModelBase
>>> heap.heap()
Partition of a set of 79843 objects. Total size = 6211716 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  37276  47  2880268  46   2880268  46 str
     1  21661  27   846520  14   3726788  60 tuple

De setref() zorgt ervoor dat bij de volgende call we alleen nieuwe objecten zien. Het inlezen van module logica zorgt voor 6.2MB extra geheugen gebruik.

>>> heap.setref()
>>> logica.configuratie.lees()
True
>>> heap.heap()
Partition of a set of 5493 objects. Total size = 461748 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0    556  10   289120  63    289120  63 dict of include.primitieven.VrijObject
     1   3767  69   134268  29    423388  92 str

Het inlezen van de configuratie van OpenAC gebruikt 461kB. Dat is misschien veel voor een bestand van 21kB grootte en 573 regels. We kunnen kijken hoe we bij al die nieuwe strings komen:

>>> (heap.heap()&str).byvia
Partition of a set of 3767 objects. Total size = 134276 bytes.
 Index  Count   %     Size   % Cumulative  % Referred Via:
     0    556  15    23704  18     23704  18 "['omschrijving']"
     1    571  15    16000  12     39704  30 "['type']"
     2    558  15    15656  12     55360  41 '.keys()[10]'
     3    557  15    15608  12     70968  53 '.keys()[1]'

Hier zien we de weg (via) voor de strings die net zijn geladen. Daar zit niet zo veel verrassends in, behalve dat zo-te-zien er weinig sharing plaatsvindt. Een VrijObject kost 520 bytes of meer (520 is de dict). Dat zie je met (heap.heap()&dict).bysize.

Je kan ook met wat gefrobel ontdekken welke objecten het zijn:

>>> v=heap.heap()&dict
>>> v.byid 
Set of 711 <mixed> objects. Total size = 326520 bytes.
 Index     Size   %   Cumulative  %   Brief
     0     1672   0.5      1672   0.5 dict of class: ..TestEnv
...
>>> _.more
...

Op deze manier kunnen we bladeren door de objecten in de set v (dat zijn dicts die recent aan de heap zijn toegevoegd). De lijst byid kunnen we indexeren om een specifiek object te vinden:

>>> v.byid[39]
Set of 1 <dict of include.primitieven.VrijObject> object. Total size = 520 bytes.
 Index     Size   %   Cumulative  %   Owner Address
     0      520 100.0       520 100.0 0x88a4d0c

Tot slot kunnen we ontdekken hoe we bij dit specifieke object kunnen komen:

>>> v.byid[39].sp
 0: heap.Root.i0_modules['logica.configuratie'].__dict__['variabelen'].__dict__['_data']['rapportage_export_folder'].__dict__

Dus, ruwweg, hebben we hier logica.configuratie.variabelen!['rapportage_export_folder'] te pakken -- of beter gezegd, de __dict__ van het VrijObject dat daardoor aangeduid wordt.

Algoritmisch Tijdgebruik

De standaardmodule timeit kan gebruikt worden om reken-intensieve functies door te voeren en tijden uit te rekenen. Het werkt het beste op echte rekenfuncties in een aparte Python module; de functie kan een of meer parameters nemen die makkelijk in te voeren zijn op de command line.

Als voorbeeld:

import timeit
print timeit.Timer(
  't1.anonimiseer("hallo","tekst")','import t1') \
  .repeat(number=10000)
print timeit.Timer(
  't2.anonimiseer("hallo","tekst")','import t2') \
  .repeat(number=10000)

Je maakt een Timer object met een fragment programmacode die uitgevoerd wordt (zonder enige context, dus je kan geen variabelen hier gebruiken die je al gedefinieerd hebt, en geen reeds geimporteerde modules). Het tweede fragment wordt precies een keer uitgevoerd om een context te maken voor het eerste fragment -- bijvoorbeeld met imports.

Het eerste fragment wordt number keer uitgevoerd (hier 10000) en de tijd die dat neemt wordt afgedrukt. Hiermee is makkelijk te zien hoe lang iets duurt of om de looptijd van twee verschillende implementaties te vergelijken.

Queries

NoteBox(tip, TODO: bijhouden van aantal queries, sqlalchemy.log, server-stats bekijken)?

Stopwatch

NoteBox(tip, TODO: bijhouden van "wall clock" tijd bij het uitvoeren van OpenAC taken.)?

NB: Zie hier voor meer over tests

Last modified 7 years ago Last modified on 06/25/19 08:47:09