Tässä vasta yritän ymmärtää c:tä paremmin. En ole tottunut vielä näihin vähän erikoisempiin vertailuasuihin jne..
Mitä tuossa whilen vertailussa oikein tapahtuu? Mikä on tuon huutomerkin merkitys?
int strcmp(const char *s1, const char *s2)
{
int ret = 0;
while (!(ret = *(unsigned char *) s1 - *(unsigned char *) s2) && *s2) s1, s2;
if (ret < 0)
ret = -1;
else if (ret > 0)
ret = 1 ;
return ret;
}
Mitä tuossa whilen vertailussa oikein tapahtuu? Mikä on tuon huutomerkin merkitys?
int strcmp(const char *s1, const char *s2)
{
int ret = 0;
while (!(ret = *(unsigned char *) s1 - *(unsigned char *) s2) && *s2) s1, s2;
if (ret < 0)
ret = -1;
else if (ret > 0)
ret = 1 ;
return ret;
}
Vastaukset 13
Vanhimmat
Noin kryptistä koodia ei aloittelijan juurikaan kannata edes yrittää ymmärtää. Joko hyväksyt sen että tuo strcmp tekee sen mitä lupaa tai kirjoitat oman selkokielisen version.
Aika yleisesti valmis c-koodi on kyllä juuri tuollaista, mutta siihen ei ole mitää järkeä pyrkiä. Joskus Aatamin ja Eevan aikana kääntäjät osasivat tehdä äärimmilleen lyhennetystä kryptisestä koodista jossain tapauksissa teoriassa tehokkaampaa koodia. Nykyiset optimoijat saavat saman tuloksen selkokielisestäkin koodista.
Huutomerkki on negaatio, eli a erisuuri kuin b on c:nä a != b.
Totta, ei kovinkaan selkeää luettavaa.
Nykyisillekin kääntäjille on tosin merkitystä miten nuo ehtokohdat muotoillaan.
Esim. for(i=3;i;--i) on eri asia kuin for(i=3;i!=0;--i)
Yleensä tuo ensinmainittu kääntyy konekieliseen muotoon:
dec eax
jnz alku
Kun taas jälkimmäinen on:
dec eax
cmp eax,0 (tai vastaava)
jne alku
Eli käskyjä tulee enemmän.
Kun käytetään ehtojen yhdistelyjä, nykyiset kääntäjät vähentävät koodin hyppimistä esim.setcc -rakenteilla, jolloin ohjelma nopeutuu.
Ohjelmaa voi myös pienentää ja nopeuttaa, kun käyttää pitkää if-elseif -ketjua korvaamaan switch -lauseen.
C-kielessä voi siis paljonkin vaikuttaa ohjelman käännökseen. Käännösten tarkastelu tosin vaatii pientä assembly -osaamista.
ja(not(luvut samat),*s2!=0)
elikkä luuppi jatkuu, kun merkit ovat samoja ja ei ole saavutettu päätösmerkkiä NULL. Ilman ei -lausetta luuppi loppuisi heti, kun merkit ovat samoja.
tentin tekijälle
Eli yritin tuon pohjalta vähän ymmärtää kyseisen funktion toimintaa, mutta ei niin ei. Ei vieläkään täysin ymmärrä. Voisiko joku kirjoittaa saman "selko koodilla" ?
Aivan kuten kirjoitin
while(!(*(BYTE*)s1-*(BYTE*)s2)&&*s2)
aina kun(ja(ei(lukujen erotus);s2:n merkki))
Jos lukujen erotus on 0, ei muuttaa sen -1.
Jos lukujen erotus on >0, ei muuttaa sen 0:ksi.
Ja -lause puolestaan antaa tosi, arvon jos molemmat lauseet ovat 0. Muutoin palautuu epätosi.
for(ret=0;ret==0&&*dst; src, dst)
ret=*(BYTE*)src-*(BYTE*)dst;
"int strcmp(const char *s1, const char *s2)
{"
Syötteenä saadaan kaksi merkkijonoa, tai oikeastaa osoitinta kahden merkkijonon alkuun. Huomaa, että tässä tehdään oletus, että merkkijonot päättyvät null-merkkiin. Muutenhan niiden pituutta ei tiedetä.
"int ret = 0;
while (!(ret = *(unsigned char *) s1 - *(unsigned char *) s2) && *s2) s1, s2;"
Jaetaan tämä useampaan palaan.
"*(unsigned char *) s1"
Ensin siinä castataan parametrina saadut merkkijonot unsigned char-tyyppisiksi osoittimiksi, otetaan osoittimen viisaama muistipaikka ja
"ret = *(unsigned char *) s1 - *(unsigned char *) s2"
vähennetään siitä toisen osoittimen muistipaikan sisältö ja laitetaan tulos ret-muuttujaan. Nythän jos ne ovat samoja, eli niissä on sama merkki, niin erotus on nolla ja ret saa arvon nolla, joka on epätosi arvo. Koska lause on suluissa ja sen edessä on huutomerkki, niin tutkitaan lauseen negaation totuusarvoa (lauseen arvohan on se mitä ret-muuttujaan sijoitetaan), eli !-merkin sulkema lauseke on totta jos ret on nolla.
Ehdossa on kaksi osaa, joista toinen on siis "&& *s2". Jotta while-ehto olisi tosi, pitää siis myös "*s2":n olla loogisesti totta, eli siinä muistipaikassa, mihin s2 viisaa pitää olla null:sta eroava merkki.
" s1, s2;"
Jos while-ehto toteutui, niin siirretään osoittimet seuraavaan muistipaikkaan ja jatketaan. Suomeksi tuossa siis kuljetetaan kahta osoitinta muistissa kunnes s2:n arvoksi tulee null tai ne eroavat toisistaan.
"if (ret < 0)
ret = -1;
else if (ret > 0)
ret = 1 ;
return ret;"
Nyt while-lause on ajettu ja ret-sisältää jonkin arvon. Nyt siis joko jossain kohdassa muistia merkkijonojen arvot eroavat toisistaan tai merkkijono s2 päättyi. Jos ret-muuttujan arvoksi tuli nolla, niin s1 ja s2 osoittivat lopuksi samaan paikkaan ja ehto-lause loppui koska s2 osoitti null:iin. Tällöin molemmat merkkijonot olivat samoja null-merkkiin asti eli ne olivat samat ja palautetaan nolla.
Jos ret ei ole nolla, niin while-ajo loppui ensimmäiseen ehtoon eli merkkijonoja ei käyty "loppuun asti". Nyt jos tutkittavasssa paikassa merkkijonossa s1 on leksikografisesti ennen merkkijonon s2 paikassa oleva arvo, niin ret on suurempi kuin nolla ja palautetaan 1, joka tarkoittaa, että merkkijonot ovat erilaisia ja s1 on aakkosjärjestyksessä ennen merkkijonoa s2. Vastaavasti toisinpäin palautetaan -1.
--
Vaikka tällaisilla kryptisillä koodeilla koitetaan usein hankkia guru-statusta, niin korostaisin muiden tapaan sitä, että guruus ei ole epäselvän koodin kirjoittamistaito. Tällaiset pätkät ovat järkyttäviä ylläpidettäviä eikä kryptisyydellä saavuteta yhtään mitään. Jos haluaa guruilla, niin sen voi tehdä vaikka algoritmeja tutkimalla tai teknistä taustatietoa hankkimalla.
Todella selkeät ohjeet täytyy myöntää, mut viimeinen kommentti:
> Vaikka tällaisilla kryptisillä koodeilla koitetaan usein hankkia guru-statusta ...
Kuten jo aiemmin vastailin, tällä EI haeta guruutta. Kyllä ton ymmäräminen kuuluu C:n perusteisiin. Tonnäköistä tulee, kun koodia pyritään optimoimaan. Jos katselee strcmp:n lähdekoodia jostain c-kirjastosta, siellä on juuri vastaava.
Tietysti harvoin tarvii mennä ihan noin pitkälle optimoinnissa. Tavallisessa koodissa vastaavanlaisesta on hyötyä korkeintaan millisekunti. Mutta jos ohjelma tekee töitä useita sekunteja tai minuutteja, optimoinnista voi olla jonkinlainen hyöty.
Iltalukemiseks vaikka:
http://www.codeproject.com/cpp/C___Code_Optimization.asp
http://www.azillionmonkeys.com/qed/optimize.html
http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25112.PDF
"Kuten jo aiemmin vastailin, tällä EI haeta guruutta. Kyllä ton ymmäräminen kuuluu C:n perusteisiin. Tonnäköistä tulee, kun koodia pyritään optimoimaan. Jos katselee strcmp:n lähdekoodia jostain c-kirjastosta, siellä on juuri vastaava. "
En viitannutkaan tähän nimenomaiseen tapaukseen. Sanoin "Vaikka tällaisilla kryptisillä koodeilla koitetaan usein hankkia guru-statusta, niin korostaisin muiden tapaan sitä, että guruus ei ole epäselvän koodin kirjoittamistaito.". Huomaatko sanan "usein" ja viittauksen, että guruus *ei* ole taito kirjoittaa *epäselvää* koodia.
Se, että strcmp-toteutukset ovat tuollaisia kirjastoissa yms. johtuu mm. siitä, että ne on kirjoitettu aikana jolloin kääntäjät olivat huonoja optimoimaan ja ne on haluttu "hakkeriviilata" mahdollisimman pitkälle huvin ja tietysti osin hyödynkin takia. Nämä toteutukset ovat yleiseen ohjelmointiin nähden hyvin poikkeuksellisessa asemassa monessakin mielessä.
"Tietysti harvoin tarvii mennä ihan noin pitkälle optimoinnissa."
Minun ei ole ikinä tarvinnut mennä tuollaiseen kikkailuun. Jos ohjelma on hidas, niin käytännössä se johtuu valitun arkkitehtuurin sopimattomuudesta tai väärän algoritmin valitsemisesta. En tiedä yhtään tapausta, milloin tämänkaltaisella optimoinnilla olisi käytännön elämässä jotain pelastettu. Jossain sulautetuissa järjestelmissä saattaa olla tuollaisilla kikkailuilla käytännön merkitystä, mutta nykyään jo puhelimetkin rupeavat olemaan alusta missä tuollaiset menettävät merkityksen muiden asioiden taakse. (Olen koodannut mm. laskutuskomponentteja jotka suorittavat tietääkseni vielä tänäkin päivänä suhteellisen järeän määrän transaktioita per sekunti. Silloinkin nopeusongelmat ratkaistiin ihan muilla keinoin kuin tällaisilla "hakkeriviilauksilla".)
Sitten on vielä se, että kompakti lähdekoodi *ei* tarkoita nopeampaa ohjelmaa vaan usein jopa päinvastoin. Koodia kirjoittaessa pitää tietää miten kääntäjä sen näkee ja monesti pitää jopa olla eksplisiittisempi ilmaisussaan, jotta voidaan tehdä päätelmiä arvoalueiden yms. suhteen ja sen perusteella optimoida.
Tuollaiset hakkeriviilaukset ovat enemmän harrastustoimintaa kuin oikeissa tietojärjestelmissä käytettyjä. Ennen kannattaa vaikka perehtyä laskennan vaativuusteoriaan, niin ymmärtää miten ongelmat käytännön elämässä oikeasti tulevan vastaan. (Toki tuollainen viilailu parantaa kykyä tuottaa ja lukea monimutkaistakin koodia, ja muutenkin tuo tiettyä "tatsia" kielen piirteisiin, eli en minä sitä turhaksi yleisellä tasolla sano, vaan totean ettei sillä ole juurikaan käytännön merkitystä.)
kiitosta vaan tästä. nyt selkeni moni juttu huomattavasti. tästä on hyvä jatkaa ;)
"Se, että strcmp-toteutukset ovat tuollaisia kirjastoissa yms. johtuu mm. siitä, että ne on kirjoitettu aikana jolloin kääntäjät olivat huonoja optimoimaan ja ne on haluttu "hakkeriviilata" mahdollisimman pitkälle huvin ja tietysti osin hyödynkin takia. Nämä toteutukset ovat yleiseen ohjelmointiin nähden hyvin poikkeuksellisessa asemassa monessakin mielessä."
"Tuollaiset hakkeriviilaukset ovat enemmän harrastustoimintaa kuin oikeissa tietojärjestelmissä käytettyjä."
En menisi kyllä sanomaan, että Intelillä, AMDllä tai mikkisoftalla tehdään noissa asioissa harrastustoimintaa tai hakkeriviilausta. Kaikilla niillä panostetaan optimoituun koodiin. Se jonka kääntäjä ja kirjastot tuottavat nopean koodin, pärjäävät. Nykyisetkään kääntäjät eivät siihen pysty ilman oikeanlaista kirjoittelua. Huomaa seuraavissa linkeissä vuosiluku (en viitsi liittää tähän kaikkien optimointikirjastojen linkkejä kun niitä on niin tuhottomasti):
http://developer.intel.com/design/processor/manuals/248966.pdf
http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25112.PDF
Jos pidät tuota harrastustoimintana, niin aika kallista harrastustoimintaa ko. firmat tekevät. Luulisi, että tekeminen olisi loppunut jo ajat sitten kannattamattomana. Sitten on vielä assemblyllä kirjoittelu. Se varmaan vasta onkin harrastustoimintaa/hakkeriviilausta. Mutta miten selittyy esim. Intel C -kääntäjän oleminen vielä markkinoilla ?
Ensimmäisenä ainakin mun mieleen tulee käyttää optimointia kuin että alkaisi miettimään olisiko Fourier -muunnoksista hyötyä ohjelman algoritmille.
Se on totta mitä sanoit, ettei sitä käytetä "oikeissa" tietojärjestelmissä. Tärkeinpänä nykyisin nähdään se, että ohjelma toimii edes jotenkuten. Koolla ja nopeudella ei olekaan sitten mitään merkitystä.
Sanoin "Nämä toteutukset ovat yleiseen ohjelmointiin nähden hyvin poikkeuksellisessa asemassa monessakin mielessä. "
Sanoin siis, että tällaiset toteutukset ovat hyvin poikkeuksellisessa asemassa, eli niitä ei käytetä kuin murto-osassa kaikista järjestelmistä. Väitän, että korkeintaan 0.1% kaikista ohjelmoijista joutuu miettimään työssään tuollaisia viilauksia niin, että niillä olisi käytännön merkitystä nopeuden kannalta.
Kaikesta päätellen olisi pitänyt vääntää ratakiskosta tämäkin asia.
"Ensimmäisenä ainakin mun mieleen tulee käyttää optimointia kuin että alkaisi miettimään olisiko Fourier -muunnoksista hyötyä ohjelman algoritmille. "
Olipa kommentti. Vastaavasti argumentoiden: jos järjestelmässä esiintyy tahmeutta, niin lähdet korjaamaan sitä esitetyn kaltaisilla viilauksilla etkä miettimään onko itse algoritmissa kenties vikaa?
Jos ei edes halua ymmärtää esitettyjä kommentteja, niin pitäisi jättää vastaamatta.
Tapaukset, joissa jotain tiettyä koodinpätkää ajetaan miljoonia kertoja, jolloin viilaus on perusteltua, ovat äärimmäisen harvinaisia. Käytännön elämässä nopeus haetaan muilla keinoilla.
"Jos pidät tuota harrastustoimintana, niin aika kallista harrastustoimintaa ko. firmat tekevät. Luulisi, että tekeminen olisi loppunut jo ajat sitten kannattamattomana."
Taas internetin pettämätöntä logiikkaa. Kaivat esiin erityistapauksia ja teet niistä yleistyksiä (huomaa, että sanoin "...ovat *enemmän* harrastustoimintaa kuin..")
Hanki enemmän sivistystä ennen viisasteluita ja opettele pitävän argumentoinnin perusteet. Juuri tämän takia en jaksa vastailla netissä esitettyihin kysymyksiin. Aina siellä on joku perusteet oppinut viisastelija ilman perspektiiviä ja alkuperäinen asia jää pätemisen tarpeen taustalle.
Nyt olen jo valmis luovuttamaan ja jättämään kommentoinnin. (Nähtäväksi jää kuin pitkään muistan tämänkertaisen opetuksen.)
Mun noin 20 vuoden ohjelmointitaustalla oon optimointihyötyihin törmäilly useasti.
Jos oot katselemassa miten ohjelmiston tietyn osan saisi nopeammaksi, ei mulle ainakaan tuu ensimmäiseks mieleen sanoo tekijälle että "koitas parannella sun algoritmiis". Ensimmäiseks ainakin yritän tehdä optimointiparannukset, johon menee max ½ - 2 tuntia. Jos puolestaan algoritmiin joudutaan tän jälkeen puuttumaan, se vie monta päivää.
Täytyy myöntää ettei kaikissa firmoissa mennä niin pitkälle, mut ainakin mä nään sen loppuasiakkaan hyötynä et ohjelma on nopea. Käytetty aika ei kuitenkaan oo ku murto-osa koko projektin työmäärästä. Tietysti jos jokainen koodari osais kirjoittaa valmiiks hyvää koodia, optimointia ei tarvitsis tehdä.
Se että sä et oo törmäilly moiseen hyötyyn, johtuu varmisti siitä, että oot tehny erilaisia ohjelmia. Ei siinä sen ihmeellisempää oo.