Koodi jõudlus Fibonacci arvude genereerimisel

30.05.2009  |  Gunnar

See kanne on küll sissejuhatuseks järgmisele, kuid otsustasin teema juures pisut põhjalikumalt peatuda ja veidike koodiga mängida. Selles kandes genereerime Fibonacci arve, kasutades kahte erinevat funktsiooni, mis oma algoritmilt on tegelikult samad. Huvipakkuvaks teemaks on funktsioonide jõudluse võrdlemine.

Järgmiseks vaatame kahte Fibonacci arvude genereerimise algoritmi teostusest.

Rekursiivne funktsioon

Esimesena võtame käsile lühema ja lihtsama versiooni algoritmi teostusest.

public int FibonacciRecursive(int x)
{
    if (x == 0 || x == 1)
        return x;
    return FibonacciRecursive(x - 1) +
             FibonacciRecursive(x - 2);
}

Kogenud programmeerijad hoiavad praegu kahe käega pead kinni ja keelduvad seda versiooni ilma jõudluse osas testimata toast välja lubama. Esimene põhjust selgub kande lõpus, teise põhjuse lobisen kohe välja – rekursiivset koodi on keerukam testida ja siluda.

“Lame” funktsioon

Nüüd vaatame funktsiooni teist versiooni. See on pikem ja keerukam lugeda, kuid see-eest ei ole see rekursiivne.

public int Fibonacci(int x)
{
    var previousValue = -1;
    var currentResult = 1

    for (var i = 0; i <= x; ++i)
    {
        var sum = currentResult + previousValue;
        previousValue = currentResult;
        currentResult = sum;
    }
    return currentResult;
}

Vaatame nüüd, kuidas need kaks versiooni samast asjast jõudluse osas käituvad.

Mõõdame jõudlust

Nüüd jõuame selle kohani, kus tuleb välja rekursiivse versiooniga seotud probleem. Muide, algajad programmeerijad ei pruugi seda probleemi avastada enne kui see produktsioonis tekib, sest paljud algajad testivad oma funktsioone küllaltki piiratud tingimustega ning korralikke mõõtmisi nad tavaliselt ei tee. Aga vaatame tulemusi.

nce 

Horisontaalses teljel on toodud funktsiooni sisendväärtused. Vertikaalsel teljel aga aeg sekundites, mis vastava sisendi juures väärtuse arvutamiseks kulus. Näeme, et orienteeruvalt 30 juures hakkab rekursiivse funktsiooni jõudlus langema eksponentsiaalselt.

Muide, see näide ei kehti ainult Fibonacci arvude genereerimise korral. Analoogset eksponentsiaalset jõudluse kadu võime märgata ka muude rekursiivsete funktsioonide korral – näiteks faktoriaali arvutamine.

Märkus. Funktsionaalsed keeled on ehitatud enamasti üles pisut teistmoodi ja nendele me ei saa siintoodut laiendada. Ma testisin rekursiivset versiooni sellise funktsionaalse keele nagu F# peal ja seal oli selle funktsiooni jõudlus umbes nagu siinsel “lamedal” versioonil.

2 kommentaari sissekandele “Koodi jõudlus Fibonacci arvude genereerimisel”

  1. tom_

    Mis on hetke seis C# kompilaatoril saba-rekursiooni toetamisel (tail recursion)? Ma kiire googeldamisega ei leidnud väga head ülevaadet. Kui leiad midagi siis huvitaks, et kui on tulnud siis millal ja ehk mõni asjalik link. Olen natsi teise platvormi peal ja ei oska kippelt hinnata .NET blogi postide sisu paikapanevust.

  2. Gunnar

    tom_, proovi neid linke, äkki on abiks:

    Tail recursion on .NET
    Siin selgitatakse, et C# kompilaator tail-recursioni osas mingeid optimeerimisi ei tee. Samas on IL tasemel võimalik vastavad muudatused sisse viia. 64-bitisel platvormil teeb siiski kompilaator vastavad kohendused ise ära. Muude kompilaatorite osas ei oska ma kaasa rääkida.

    Paar linki veel MSDN-i blogidest:

    Tail call performance on x86
    Tail call JIT conditions

    Äkki on abiks :)

Kommenteeri

sulge
Saada link e-postiga

© DT 2012 | Creative Commons Attribution-Noncommercial 3.0 License | WordPress