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.
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.
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.
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.
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).
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ä.
VB6 ja tuollainen nopeusoptimointi on kyllä suht absurdi yhdistelmä.
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
Varsin vaikuttavaa :)
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ä.
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
jaTesti2
kirjoitettaisiin aukiForm_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.
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.
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.
funktio kirjoitti:
Eikä sitä tarvitse edes tehdä itse, jos kääntäjä tukee inline-kutsuja.
Jos.
Aihe on jo aika vanha, joten et voi enää vastata siihen.