Kirjautuminen

Haku

Tehtävät

Keskustelu: Yleinen keskustelu: Yhden proseduurin ohjelma

Sivun loppuun

rautamiekka [20.05.2009 22:07:08]

#

Tuli tuossa mieleen laittaa mahdollisimman vähälukuiseksi proseduurien määrä ohjelmassa, että onko softan suorituskyvyn kannalta LIIKAA jos laittaa yhteen tai kahteen proseduuriin koko softan ja se looppaa sitä/niitä ? Tietenkin koodi muuttaa muuttujia että jotain oikeasti tapahtuisi.

VB(6):lla tehtyä softaa lähinnä tarkoitan, mutta varmaan se muissakin kielissä vaikuttaa.

Merri [20.05.2009 22:25:10]

#

Eipä se nyt niinkään suorituskykyyn vaikuta, vaan ennemminkin koodin luettavuuteen. Useampia proseduurejahan käytetään nimenomaan luokittelemaan usein käytettyjä osasia omiksi kokonaisuuksikseen tai selkeästi erottelemaan jokin palanen omilleen selkeyttämisen vuoksi. Yhdellä proseduurilla eläessä (käytännössä Sub Main tai vastaava) joutuu käyttämään esim. Gotoa, joka on yleisesti todettu huonoksi tavaksi nykyaikaisia ohjelmia tehdessä juuri luettavuuden vaikeutumisen takia. Tosin voi sillä myös varomaton ohjelmoija saada aikaan esimerkiksi muistivuotoja.

Antti Laaksonen [20.05.2009 22:50:32]

#

Proseduurin vaihtaminen vie jonkin verran aikaa, joten jos kaiken koodin kirjoittaa samaan proseduuriin, tuloksena on nopein mahdollinen ohjelma. Tämä ei ole kuitenkaan käytännössä järkevää, koska nopeusetu on hyvin pieni mutta koodi muuttuu helposti sekavaksi.

rautamiekka [20.05.2009 22:52:42]

#

Antti Laaksonen kirjoitti:

Proseduurin vaihtaminen vie jonkin verran aikaa, joten jos kaiken koodin kirjoittaa samaan proseduuriin, tuloksena on nopein mahdollinen ohjelma. Tämä ei ole kuitenkaan käytännössä järkevää, koska nopeusetu on hyvin pieni mutta koodi muuttuu helposti sekavaksi.

Eli, ainoa keino todeta mainitsemasi asiat vedenpitävästi on testata ne ? Jotain samantapaista kävi mielessä, kuin sanoit.

Merri [21.05.2009 04:15:20]

#

Ei sitä tarvitse testata, se on testattu ja todettu moneen kertaan, ja testaamattakin asia on aika selvä jo teorian tasolla. Proseduuriton koodi on nopeampaa, ja taas moduulien proseduurit ovat nopeampia kuin luokkien ja objektien proseduurit, jotka puolestaan ovat muistaakseni nopeampia kuin API-kutsut (VB6:ssa).

Metabolix [21.05.2009 10:53:54]

#

Antti Laaksonen kirjoitti:

Proseduurin vaihtaminen vie jonkin verran aikaa, joten jos kaiken koodin kirjoittaa samaan proseduuriin, tuloksena on nopein mahdollinen ohjelma.

Ohjelmasta ei itse asiassa tuolla mainitulla tavalla tule suinkaan "nopein mahdollinen". Myös muistin lukeminen vie aikaa, ja jos tiettyjä toistuvia asioita on kirjoitettu moneen kertaan, kodin koko voi kasvaa niin suureksi, ettei koko ohjelma mahdu kerralla prosessorin välimuistiin, jolloin toiminta hidastuu, kun koodia joudutaan tavallista useammin hakemaan keskusmuistista.

Jos ohjelman yrittää kirjoittaa kerralla tuohon "tiiviiseen" muotoon (josta luultavasti tulee lopulta huomattavasti pidempi), voi myös päätyä niin huonoihin ohjelmointiratkaisuihin, että ohjelma hidastuu jo tästä.

Deffi [21.05.2009 12:16:24]

#

VB6 ja tuollainen nopeusoptimointi on kyllä suht absurdi yhdistelmä.

Antti Laaksonen [21.05.2009 12:53:57]

#

Tässä on yksi testiohjelma, jolla nopeuksia voi vertailla. Ohjelmaan kuuluu kaksi aliohjelmaa, jotka molemmat etsivät taulukosta lähinnä toisiaan olevat tason pisteet. Testi1 laskee kahden pisteen etäisyyden kutsumalla funktiota Matka, joka kutsuu edelleen funktiota Nelio. Testi2 laskee etäisyyden samalla tavalla mutta ilman funktioita. (Tarkasti ottaen lasketaan etäisyyden neliö, mikä ei vaikuta vertailuun.)

Minun koneellani Testi1 vie aikaa 10,4 sekuntia ja Testi2 vie aikaa 0,7 sekuntia. Lähes 15-kertainen ero ei ole minusta merkityksetön. Jos aineisto olisi suurempi, ero voisi olla yksi päivä vastaan kaksi viikkoa.

Option Explicit

Const KOKO = 10000

Dim Data(KOKO, 2) As Double

Function Nelio(a As Double) As Double
    Nelio = a * a
End Function

Function Matka(x1 As Double, y1 As Double, x2 As Double, y2 As Double) As Double
    Matka = Nelio(x2 - x1) + Nelio(y2 - y1)
End Function

Sub Testi1()
    Dim i As Integer, j As Integer
    Dim pienin As Double, uusi As Double
    ' etsitään kahden pisteen pienin etäisyys
    pienin = 999
    For i = 1 To KOKO
        For j = i + 1 To KOKO
            uusi = Matka(Data(i, 1), Data(i, 2), Data(j, 1), Data(j, 2))
            If uusi < pienin Then pienin = uusi
        Next
    Next
    Print "Tulos 1: " & Sqr(pienin)
End Sub

Sub Testi2()
    Dim i As Integer, j As Integer
    Dim pienin As Double, uusi As Double
    ' etsitään kahden pisteen pienin etäisyys
    pienin = 999
    For i = 1 To KOKO
        For j = i + 1 To KOKO
            uusi = (Data(j, 1) - Data(i, 1)) * (Data(j, 1) - Data(i, 1)) + _
                   (Data(j, 2) - Data(i, 2)) * (Data(j, 2) - Data(i, 2))
            If uusi < pienin Then pienin = uusi
        Next
    Next
    Print "Tulos 2: " & Sqr(pienin)
End Sub

Private Sub Form_Click()
    Dim i As Integer, aika As Double
    ' valitaan joukko pisteitä satunnaisesti
    Randomize 12345
    For i = 1 To KOKO
        Data(i, 1) = Rnd
        Data(i, 2) = Rnd
    Next
    ' suoritetaan mittaukset
    Cls
    aika = Timer
    Testi1
    Print "Aika 1: " & Timer - aika
    DoEvents
    aika = Timer
    Testi2
    Print "Aika 2: " & Timer - aika
    DoEvents
End Sub

rautamiekka [21.05.2009 20:30:52]

#

Varsin vaikuttavaa :)

os [21.05.2009 20:58:32]

#

Vaikuttavaa joo, mutta älä silti missään nimessä ota proseduurien/funktioiden/metodien määrän minimointia ikinä yleiseksi tavoitteeksi. Nopeusero on merkittävä ainoastaan tällaisten äärimmäisten pullonkaulakohtien tapauksessa. Näissä myös optimointi voidaan tehdä helposti jälkikäteen.

Mitä enemmän prosessointia funktion sisällä on, sitä vähemmän funktiokutsulla on merkitystä. Jos esimerkiksi Antin koodissa Testi1 ja Testi2 kirjoitettaisiin auki Form_Click:in sisälle, ei ohjelma hidastuisi käytännössä yhtään, mutta koodin rakenne muuttuisi heti epäselvemmäksi. Lisäksi jos funktiokutsu ei ole kriittisessä kohdassa (kuten Antin esimerkissä), ei nopeuserolla myöskään ole koko ohjelman nopeuden kannalta merkitystä.

rautamiekka [21.05.2009 21:06:24]

#

os kirjoitti:

Vaikuttavaa joo, mutta älä silti missään nimessä ota proseduurien/funktioiden/metodien määrän minimointia ikinä yleiseksi tavoitteeksi. Nopeusero on merkittävä ainoastaan tällaisten äärimmäisten pullonkaulakohtien tapauksessa. Näissä myös optimointi voidaan tehdä helposti jälkikäteen.

Mitä enemmän prosessointia funktion sisällä on, sitä vähemmän funktiokutsulla on merkitystä. Jos esimerkiksi Antin koodissa Testi1 ja Testi2 kirjoitettaisiin auki Form_Click:in sisälle, ei ohjelma hidastuisi käytännössä yhtään, mutta koodin rakenne muuttuisi heti epäselvemmäksi. Lisäksi jos funktiokutsu ei ole kriittisessä kohdassa (kuten Antin esimerkissä), ei nopeuserolla myöskään ole koko ohjelman nopeuden kannalta merkitystä.

Totta joka(inen) sana(si).

Toisaalta, suuri määrä erilaisia proseduureja/muuttujia tekee softasta helposti epäselvän kuten olen huomannut monissa pikkasenkin isoimmissa VB6-softissa.

Torgo [21.05.2009 21:54:46]

#

Merri kirjoitti:

Gotoa, joka on yleisesti todettu huonoksi tavaksi nykyaikaisia ohjelmia tehdessä juuri luettavuuden vaikeutumisen takia. Tosin voi sillä myös varomaton ohjelmoija saada aikaan esimerkiksi muistivuotoja.

Eikä Goton huonot puolet siihen lopu. Ei pelkästään muistivuoto ole ongelma vaan juuri päinvastainen. Eli yritetään käyttää varaamatonta muistia. Ohjelman suoritus helposti hyppää ohi muistien varauksen ja muuttujien määrittelyn. Toki Gotoa voi käyttää fiksustikin ja esim. virheidenkäsittelyssä sille voisi löytää käyttöä. Senkin tosin voi tehdä vähintään yhtä hyvin ilmankin Gotoa.

os kirjoitti:

Vaikuttavaa joo, mutta älä silti missään nimessä ota proseduurien/funktioiden/metodien määrän minimointia ikinä yleiseksi tavoitteeksi. Nopeusero on merkittävä ainoastaan tällaisten äärimmäisten pullonkaulakohtien tapauksessa. Näissä myös optimointi voidaan tehdä helposti jälkikäteen.

Aivan. Yleensäkin optimointi tulisi tehdä jälkikäteen, jos sille ilmenee tarvetta. Ennenaikainen optimointi lähes aina heikentää koodia jollain tavalla.

Yleensä (lähes aina) pullonkaulat johtuvat huonosta rajapintasuunnittelusta eikä niinkään yksittäisistä käskyistä tai funktiokutsuista. Jos ohjelman rajapinta on suunniteltu järkeviin kokonaisuuksiin, on optimointikin helppoa. Sinun tarvitsee vain vaihtaa pullonkaulana toimiva proseduuri tehokkaampaan versioon. Ja jos kriittiseksi muodostuu itse funktiokutsu, niin ainahan sen voi vaihtaa makroon tai kaikkein epätoivoisimmassa tapauksessa myöhemmin liittää osaksi korkeamman tason proseduuria.

Proseduureilla voi jo ihan sinälläänkin nopeuttaa ohjelmaa. Kuten Metabolix totesi, niin tarvittavat pätkät löytyvät välimuistista selvästi nopeammin kuin keskusmuistista. Lisäksi monissa presessoriarkkitehtuureissa on erityinen tuki muistialueen alussa olevaan muistiin jota voidaan käsitellä rekisterien tapaan huomattavasti muuta muistia nopeammin. Yleensä tuota muistia käytetään paikallisille tai muuten vain usein käytetyille muuttujille. Jos kaikki tungetaan samaan proseduuriin, on aika epätodennäköistä että paikalliset muuttujat mahtuisivat tuolle muistialueelle ja muuttujien prosessointi kestäisi selvästi kauemman. Toki onnistuu uudelleenkäyttämällä samaa muuttujaa moneen eri tarkoitukseen, mutta onko sekään järkevää että on yksi muuttuja 'x' jota käytetään kaikkeen?

Yleensäkin optimointi matalammalla tasolla kuin rajapintatasolla on enemmänkin yritä ja erehdy periaatteella toimimista. Se mikä toimii yhdellä kääntäjällä, yhdessä ohjelmassa, tietyllä prosessorilla voi ollakin aivan päinvastoin jossain toisessa kontekstissa. Esimerkiksi pätkä:

for(i=0; i<4; ++i) { data[i] = get_input_status(i); }

olikin käyttämälläni kääntäjällä nopeusoptimoituna yllättäen nopeampi kuin:

data[0] = get_input_status(0);
data[1] = get_input_status(1);
data[2] = get_input_status(2);
data[3] = get_input_status(3);

Intuitiolla olisi voinut kuvitella jälkimmäisen tavan olevan nopeampi tai vähintään yhtä nopea.

jlaire [21.05.2009 22:28:07]

#

Torgo kirjoitti:

Ja jos kriittiseksi muodostuu itse funktiokutsu, niin ainahan sen voi vaihtaa makroon tai kaikkein epätoivoisimmassa tapauksessa myöhemmin liittää osaksi korkeamman tason proseduuria.

Eikä sitä tarvitse edes tehdä itse, jos kääntäjä tukee inline-kutsuja. Ei mikään tajuttoman vaikea optimointi toteuttaa ja siitä on monesti hyötyä, joten luulisi useimmista nykyaikaisista kääntäjistä löytyvän.

rautamiekka [25.05.2009 16:18:10]

#

funktio kirjoitti:

Eikä sitä tarvitse edes tehdä itse, jos kääntäjä tukee inline-kutsuja.

Jos.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta