Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: 8th: Työn jakaminen useammalle säikeelle

Sivun loppuun

jalski [23.11.2020 17:59:34]

#

Pistetäänpä esimerkkinä miten säikeiden avulla saadaan huomattava nopeutus ohjelman suoritusaikaan, jos ongelma voidaan jakaa useaan itsenäiseen osaan.

Lasketaan tekstitiedoston merkkien lukumäärät ja tulostetaan aakkosjärjestyksessä:

: print-character-frequency
  swap over m:@ a:len nip rot -1 s:@ dup 31 127 n:between if
    drop "'%s'" s:strfmt
  else
    nip "<%d>" s:strfmt
  then
  "%s: %d\n" s:strfmt . ;

: app:main
  0 args "Give filename as param" thrownull f:slurp >s "" s:/ ' noop a:group
  m:keys ' s:cmp a:sort ' print-character-frequency a:each! 2drop
  bye ;

Kokeiltaessa tekstitiedostolla, mikä sisältää "War and Peace" kirjan tekstin suoritusaika on omalla tietokoneellani n. 58 sekuntia. Täytyyhän sitä parempaankin pystyä?

Jakamalla työn useamman säikeen kesken homma nopeutuu huomattavasti ja suoritusaika putoaa n. 58 sekunnista vähän yli neljään sekuntiin:

needs map/iter

8 var, numtasks
var tasks

0 args "Give filename as param" thrownull
f:slurp >s s:len numtasks @ n:/ n:ceil 1 a:close s:/ a:len numtasks ! constant work

m:new constant result

: print-character-count  \ m s -- m
  dup -1 s:@ dup 31 127 n:between if
    drop "'%s'" s:strfmt
  else
    nip "<%d>" s:strfmt
  then
  -rot m:@ rot
  "%s: %d\n" s:strfmt . ;

: print-results
  tasks @ a:len ( a:pop t:result nip ( result -rot m:[]! drop ) m:each drop ) swap times drop
  result ( nip array? if ' n:+ 0 a:reduce then ) m:map
  m:keys ' s:cmp a:sort ' print-character-count m:iter drop ;

: task  \ slice -- m
  "" s:/ ' noop a:group
  ( nip a:len nip ) m:map ;

: start-tasks
  a:new
  ( work a:pop nip 1 ' task t:task-n a:push ) numtasks @ times
  tasks ! ;

: wait-tasks
  tasks @ t:wait ;

: app:main
  start-tasks
  wait-tasks
  print-results
  bye ;

Grez [24.11.2020 11:06:57]

#

Esimerkin yhteydessä voisi olla hyvä avata myös mihin suoritusajan lyhentyminen perustuu.

Tyypillisesti tehtävien suoritusnopeuden rajoittaa jokin seikka esim. CPU tai IO, tai jokin näiden yhdistelmä.

Jos tehtävän nopeutta rajoittaa CPU, niin silloin sen jakamisesta osatehtäviksi tehtäviksi on hyötyä, mikäli koneessa on useampia prosessoreja tai coreja tai yksi core pystyy ajamaan useita säikeitä. Esimerkiksi kuvailemasi hyöty saattaisi tulla jos koneessa olisi 7 corea joista kukin pystyy ajamaan 2 säiettä. Nopeutus voisi olla parhaimmillaan lähes 14 kertainen, esim. 58s -> 4,1s)

Jos tehtävän suorittamista rajoittaa IO, niin silloin jakamisesta voi olla hyötyä, CPU voi suorittaa muita tehtäviä sillä aikaa, kun odotetaan IO-haun tulosta. Lisäksi hyötyä voi siitä että voidaan tehdä useita IO-pyyntöjä rinnakkain ja jäädä odottamaan kunkin vastausta.

jalski kirjoitti:

"War and Peace" kirjan tekstin suoritusaika on omalla tietokoneellani n. 58 sekuntia. Täytyyhän sitä parempaankin pystyä?

Sinänsä itselläkin ensimmäinen ajatus on, että eihän 3 megan tiedoston käsittelyyn noin kauaa pitäisi mennä. Ensimmäinen ajatus itsellä ei kuitenkaan olisi käyttää säikeitä vaan katsoa mikä siinä algoritmissa on vikana. Tein C# -ohjelman, joka laskee merkit, ja tulostaa kaikki merkit ja määrät yleisyysjärjestyksessä. Lukemisineen ja tulostamisineen kaikkineen aikaa meni yksisäikeisenä 0,126s. Eli jostain syystä käyttämäsi algoritmi on noin 500 kertaa hitaampi kuin tehtävän suoraviivainen toteuttaminen ilman optimointia.

using System;
using System.Diagnostics;
using System.Linq;

namespace CharCounter
{
    class Program
    {
        static void Main(string[] args)
        {
            var s = new Stopwatch();
            s.Start();
            foreach (var m in System.IO.File.ReadAllText("warandpeace.txt")
                .ToCharArray()
                .GroupBy(c=>c).Select(g=>new { merkki = g.Key, kpl = g.Count() })
                .OrderByDescending(c=>c.kpl))
            {
                Console.WriteLine($"{m.merkki}: {m.kpl}");
            }
            s.Stop();
            Console.WriteLine($"Took {s.ElapsedMilliseconds}ms");
        }
    }
}

jalski [24.11.2020 20:03:19]

#

Grez kirjoitti:

Sinänsä itselläkin ensimmäinen ajatus on, että eihän 3 megan tiedoston käsittelyyn noin kauaa pitäisi mennä. Ensimmäinen ajatus itsellä ei kuitenkaan olisi käyttää säikeitä vaan katsoa mikä siinä algoritmissa on vikana. Tein C# -ohjelman, joka laskee merkit, ja tulostaa kaikki merkit ja määrät yleisyysjärjestyksessä. Lukemisineen ja tulostamisineen kaikkineen aikaa meni yksisäikeisenä 0,126s. Eli jostain syystä käyttämäsi algoritmi on noin 500 kertaa hitaampi kuin tehtävän suoraviivainen toteuttaminen ilman optimointia.

Alkuperäisen toteutuksen ei varsinaisesti ollutkaan tarkoitus olla erityisen nopea vaan yksinkertainen ja helppo toteuttaa. Pitää huomioida, että C# toteutuksesi käyttää char taulukkoa ja omani taulukkoa, mikä on toteutukseltaan oikeasti enempi lista kuin perinteinen taulukko. Lisäksi se sisältää yhden merkin pituisia merkkijonoja. Huomioi, että toteutuksessani luodaan aika tavalla uusia merkkijonoja ison tiedoston ollessa kyseessä! Myös merkkijonojen toteutus on erilainen, C#:n käyttämä merkistö on muistaakseni UTF-16 ja 8th:lla UTF-8. Jos nopeus olisi ollut lähtökohtana ja tietäisi, että käsiteltäisiin vain ascii-merkistöä niin käyttäisin varmaan puskuria ja käsittelisin suoraan peräkkäin tallennettuna olevia tavuja. Omasta mielestäni tuo muutaman sekunnin suoritusaika on kuitenkin riittävä kohtuullisen kokoisen tiedoston ollessa kyseessä.

Grez [25.11.2020 08:37:48]

#

jalski kirjoitti:

Alkuperäisen toteutuksen ei varsinaisesti ollutkaan tarkoitus olla erityisen nopea vaan yksinkertainen ja helppo toteuttaa.

Itse tein myös mahdollisimman yksinkertaisen ja helpon toteuttaa. Tarkoitatko sanoa että edes kohtuullisen järjellisen algoritmin toteuttaminen 8th:lla on joko monimutkaista tai vaikeaa tai sekä että?

jalski kirjoitti:

Pitää huomioida, että C# toteutuksesi käyttää char taulukkoa ja omani taulukkoa, mikä on toteutukseltaan oikeasti enempi lista kuin perinteinen taulukko.

No jos tuossa omassa toteutuksessa käytän taulukon asemesta listaa, niin suoritusaika kasvaa 126ms -> 140ms eli sen ei pitäisi olla merkittävä tekijä.

jalski kirjoitti:

Lisäksi se sisältää yhden merkin pituisia merkkijonoja. Huomioi, että toteutuksessani luodaan aika tavalla uusia merkkijonoja ison tiedoston ollessa kyseessä!

Tästä tietysti herää kysymys että eikö ole melko järjenvastaista tehdä läjä yhden merkin pituisia merkkijonoja, jos tarkoitus on laskea yksittäisiä merkkejä. Kokeilin silti omassanikin luoda jokaisesta merkistä merkkijonon ja laittaa ne listaan, niin suoritussaika kasvoi 592ms:iin.

jalski kirjoitti:

Myös merkkijonojen toteutus on erilainen, C#:n käyttämä merkistö on muistaakseni UTF-16 ja 8th:lla UTF-8.

Niin no tällä ei liene ole merkitystä, jos tarkoituksesi ei ole sanoa että 8th:n merkkijonototeutus on 100 kertaa hitaampi kuin normaalisti ja näinollen 8th ei kannattaisi käyttää merkkijonojen käsittelyyn lainkaan. .Net käyttää tosiaan sisäisesti UTF-16:a ja tiedosto joka luettiin oli UTF-8-muodossa.

jalski kirjoitti:

Jos nopeus olisi ollut lähtökohtana ja tietäisi, että käsiteltäisiin vain ascii-merkistöä niin käyttäisin varmaan puskuria ja käsittelisin suoraan peräkkäin tallennettuna olevia tavuja.

Surullista jos tällaisia kikkailuja joutuu tekemään että pääsee järjellisiin tuloksiin. Sinänsähän tuolla oli runsaasti ascii-merkistöön kuulumattomia merkkejä, joten tuollainen ratkaisu olisi vaatinut lisäkikkailuja.

jalski [25.11.2020 10:31:54]

#

Grez kirjoitti:

No jos tuossa omassa toteutuksessa käytän taulukon asemesta listaa, niin suoritusaika kasvaa 126ms -> 140ms eli sen ei pitäisi olla merkittävä tekijä.

Omassa versiossani niitä listoja ja merkkijonoja luodaan aikamoinen määrä! Typerää tehdä henkilökohtainen hyökkäys esimerkistä jonka tarkoitus oli näyttää miten säikeiden avulla saadaan merkittävä nopeutus, jos tehtävä voidaan jakaa useampaan itsenäiseen osaan. En missään edes sanonut, että käyttämäni tapa merkkien laskemiseen olisi järkevä tai nopea!

En ole väittänyt 8th:ta maailman nopeimmaksi, kyseessä on kuitenkin osittain tulkkaava ohjelmointikieli ja sanat käännetään konekielelle kun ohjelma käynnistetään. Ohjelma ei siis ole valmiiksi konekielisessä muodossa tai edes tavukoodi muodossa! Eli järkikin sanoo, että tuohon 126 ms suoritusaikaan on aika vaikea päästä...

Grez [25.11.2020 11:42:08]

#

On hienoa, että tänne laitettiin esimerkki siitä, miten 8th kielellä voi tehdä säikeistettyä koodia.

(Unohdin ekaan viestiin antaa tuon positiivisen palautteen ja syöksyin suoraan jatkopohdintaan.)

jalski kirjoitti:

En missään edes sanonut, että käyttämäni tapa merkkien laskemiseen olisi järkevä tai nopea!

Jos olisin ymmärtänyt, että on tehty tarkoituksella ylettömän hidas koodi säikeistyksen esittelemiseen, en olisi varmaan edes lähtenyt kommentoimaan.

Nyt kuitenkin ymmärsin lähtökohdan virheellisesti. Ajattelin, että lähtökohtana oli liian hidas koodi ("suoritusaika on omalla tietokoneellani n. 58 sekuntia. Täytyyhän sitä parempaankin pystyä?") ja yritetään keksiä keinoja sen nopeuttamiseen.

Sen vuoksi halusin sanoa, että säikeistys on erittäin hyvä työkalu monissa tilanteissa, mutta ei kuitenkaan läheskään aina se paras ratkaisu hitauden korjaamiseen.

jalski kirjoitti:

Typerää tehdä henkilökohtainen hyökkäys esimerkistä jonka tarkoitus oli näyttää miten säikeiden avulla saadaan merkittävä nopeutus

Luin tuon viestini uudelleen ja myönnän, että olisin voinut muotoilla sen rakentavampaan sävyyn. Kirjoitin sen tosiaan typerästi ja pahoittelen sitä. Lähtökohtanani oli kuitenkin halu käydä keskustelua siitä, missä tilanteissa on järkevää käyttää säikeistystä ja milloin nopeutusta tulisi hakea muilla keinoilla.

Minua kiinnostaa yleisellä tasolla mistä hitaus johtuu (eli esim. CPU bound vai IO bound) vai johtuuko se esimerkiksi siitä, että algoritmi hidastuu eksponentiaalisesti syötekoon funktiona.

Eli jos tietokoneessa on prosessori(t), joka voi suorittaa kerralla 14+ säiettä, niin silloinhan säikeistyksen tuoma nopeutus vaikuttaa kohtuullisen luontevalta.

Esimerkiksi koneessa, jolla nyt kirjoitan, on 6-ytiminen prosessori, joka näkyy 12 "loogisena prosessorina". Eli se pystyy suorittamaan 12 säiettä samanaikaisesti. Hyvin rinnakkaistuvilla kuormilla saan säikeistyksellä helposti jopa lähes 10 kertaisen nopeutuksen.

Jos sen sijaan prosessori pystyy suorittamaan vaikkapa vain 4 säiettä kerralla ja säikeistämällä CPU bound tehtävän saa 14 kertaisen nopeutuksen, niin silloin tulee vahvasti vaikutelma, että algoritmissa on jotain vikaa ja nopeutusta tulisi hakea pikemminkin algoritmia parantamalla.

jalski kirjoitti:

En ole väittänyt 8th:ta maailman nopeimmaksi

Itse ajattelin asiaa lähinnä siltä kannalta, että mielestäni olisi omituista, jos pelkkä ohjelmointikielen vaihto aiheuttaisi 100+ -kertaisen nopeuseron. Olipa sitten kyseessä tulkattu tai käännetty koodi. (Tosin kirjoitit, että sanat käännetään konekielelle käynnistettäessä, eli kyseessä olisi kuitenkin käännetty koodi?)

jalski [25.11.2020 12:47:10]

#

Grez kirjoitti:

jalski kirjoitti:

En ole väittänyt 8th:ta maailman nopeimmaksi

Itse ajattelin asiaa lähinnä siltä kannalta, että mielestäni olisi omituista, jos pelkkä ohjelmointikielen vaihto aiheuttaisi 100+ -kertaisen nopeuseron. Olipa sitten kyseessä tulkattu tai käännetty koodi. (Tosin kirjoitit, että sanat käännetään konekielelle käynnistettäessä, eli kyseessä olisi kuitenkin käännetty koodi?)

Sanalla tarkoitan Forth tyyliin siis tuota kaksoispisteen ja puolipisteen väliin tulevaa kokonaisuutta. Käytin laiskuuttani valmiita listan ja map rakenteen käsittelyyn tarkoitettuja sanoja tekemään työn. Näistä käyttämistäni jokainen palauttaa aina uuden listan tai map:in. Tuossa siis luodaan ja vapautetaan aikamoinen määrä merkkijonoja, listoja ja map rakenteita. Nopeus oli kuitenkin mielestäni itselleni riittävä ja mieleeni ei tullut, että joku keksisi mollata käyttämääni ohjelmointikieltä oman laiskan toteutukseni takia. Kyseessä on kuitenkin oikeasti aika monipuolinen työkalu.

Metabolix [25.11.2020 14:00:22]

#

Säikeistä on hyvä saada esimerkkejä turvallisilla toteutustavoilla (lyhyt koodi, ei sekaannuksia muistinkäytön tms. riippuvuuksien kanssa). Kuten yleensäkin, toivoisin esimerkkiin enemmän kuvausta ja kommentteja mm. siitä, mitkä ovat ne keskeiset sanat ja rakenteet, jotka tässä mahdollistavat oikeanlaisen säikeiden käytön.

Hyvä huomio on silti myös se, että vaikka säikeillä voi nopeuttaa ohjelman toimintaa (kuten tässä esimerkissä), kannattaa silti tutkia myös muut nopeuttamisen mahdollisuudet. Jos lähtökohta on, että ohjelman pitäisi olla nopeampi, yleensä suositeltava ensimmäinen askel on tehokkaammin toteutettu algoritmi (en ota kantaa ohjelmointikieleen!) ja vasta toinen askel on resurssien lisääminen usealla säikeellä tai koneella tai pilvipalvelulla. Jos kaikki ohjelmistonkehittäjät ajattelevat, että ohjelman hitauden voi paikata käyttämällä koneen kaikki mahdolliset tehot, kone on pian aivan jumissa.

Grez [25.11.2020 14:19:51]

#

jalski kirjoitti:

mieleeni ei tullut, että joku keksisi mollata käyttämääni ohjelmointikieltä oman laiskan toteutukseni takia. Kyseessä on kuitenkin oikeasti aika monipuolinen työkalu.

Mielestäni en mollannut ohjelmointikieltä vaan nimenomaan koetin korostaa ongelman todennäköisemmin olevan käytetyssä algorimitmissa kuin ohjelmointikielessä tai siinä käytettävissä olevissa tietorakenteissa. Olit selitellyt hitautta kaikenlaisilla muilla seikoilla, paitsi itse algoritmilla, jolloin sorruin vähän esittämään kärjekkäitä kommenteja, että ei ne nyt varmaan 8th:ssakaan niin huonosti voi olla, kuin mitä annoit ymmärtää.

The Alchemist [26.11.2020 05:52:02]

#

jalski kirjoitti:

Typerää tehdä henkilökohtainen hyökkäys esimerkistä jonka tarkoitus oli näyttää miten säikeiden avulla saadaan merkittävä nopeutus, jos tehtävä voidaan jakaa useampaan itsenäiseen osaan. En missään edes sanonut, että käyttämäni tapa merkkien laskemiseen olisi järkevä tai nopea!

Kuten aiemmatkin käyttäjät ovat sanoneet, niin monisäikeistykseen turvautumisen pitäisi ennemmin olla aivan se viimeinen keino sovelluksen nopeuttamiseen kuin että roiskaistaan seinälle ensimmäisenä mieleen tullut algoritmi, todetaan se hitaaksi, ja sitten lähdetään pilkkomaan koodia säikeistettävään muotoon.

Monisäikeistys tekee koodista usein monimutkaista ja virheherkkää, ja sen hallitseminen voi olla vaikeaa. Sovellus voi vaikuttaa toimivan oikein, mutta välillä se sitten tuottaakin virheellisen tuloksen jopa samalla syötteellä. Tai jopa kaatuu.

jalski [26.11.2020 08:21:45]

#

The Alchemist kirjoitti:

Monisäikeistys tekee koodista usein monimutkaista ja virheherkkää, ja sen hallitseminen voi olla vaikeaa. Sovellus voi vaikuttaa toimivan oikein, mutta välillä se sitten tuottaakin virheellisen tuloksen jopa samalla syötteellä. Tai jopa kaatuu.

Tässä kyseisessä tapauksessa jokaiselle säikeelle annetaan oma itsenäinen osuus työstä, jolloin kaikki monimutkaisuus käytännössä katoaa. Toiminnan oikeellisuuden voi kokeilla aivan hyvin yhdellä säikeellä. Eri asia, jos säikeet käsittelisivät samaa tietoa ja hommat pitäisi sovittaa yhteen lukkojen avulla tai jollain muulla tavalla.

jalski [26.11.2020 11:56:31]

#

Kirjoitanpa nyt sitten paremman kommentoidun esimerkin säikeiden käytöstä, missä lasketaan 80000 ensimmäistä Fibonaccin lukua nopeasti.

Tällä kertaa nopeuden pitäisi olla riittävä C#-ohjelmoijallekin. Työ on helppo jakaa säikeiden kesken kun huomataan, että "Fast Doubling" algoritmi laskee itse asiassa kaksi peräkkäistä lukua kerralla!

Grez [26.11.2020 12:43:08]

#

Jos meinaat laskea kaikki luvut tietyltä väliltä, niin fast doubling kuluttaa aika paljon hukkaan CPU-aikaa.

Jännittävä nähdä millaista säikeistystrategiaa tulet käyttämään.

Sinänsä 80 000 ensimmäisen fibonaccin luvun laskemiseen näyttää menevän aikaa luokkaa 170ms yhdellä säikeellä naiivilla toteutuksella, joten tässäkään ei säikeistäminen heti ensimmäisenä vaikuttaisi tarpeelliselta. Toki esimerkki on sinänsä hyvä että pelkkää yhtä lukuarvoa vaihtamalla voidaankin laskeakin vaikka 8 000 000 ensimmäistä. Ja myöskin jos jokaisen luvun haluaa tulostaa 10-kantaisena, niin siinä sitten saa menemään reilusti lisää aikaa.

jalski [26.11.2020 13:00:00]

#

Grez kirjoitti:

Jos meinaat laskea kaikki luvut tietyltä väliltä, niin fast doubling kuluttaa aika paljon hukkaan CPU-aikaa.

Jännittävä nähdä millaista säikeistystrategiaa tulet käyttämään.

Meinasin antaa säikeille omat lukuvälit työskenneltävää. Tuo Fast Doubling siis antaa säikeelle kaksi lähtöarvoa, joiden pohjalta lähteä iteratiivisesti lukuja laskemaan.

Meinasin jo pistää ennen kuin teit lisäyksen viestiin, että ei millään kyllä 170 ms laske ja tulosta lukuja tiedostoon! 😄

Grez [26.11.2020 13:44:56]

#

Joo näköjään naiivilla menetelmällä laskien, käyttäen 10-kantaiseksi tulostamiselle optimoitua menetelmää laskemiseen meni jo 1,7s. Laskemiseen ja tiedostoon tulostamiseen yhteensä 8,5s. Tuloksena 638 mebitavun tiedosto.

Eli tuonhan voisi säikeistää niin että yksi säie laskee ja 4 säiettä hoitaa tulostusta (tässäkin kuitenkin suurin osa ajasta menee lukujen muuntamiseen eikä varsinaiseen levylle kirjoitukseen), jolloin koko homma voisi sujua noin 2 sekunnissa koneessa joka pystyy suorittamaan 5 säiettä samanaikaisesti.

Toki tehokkaammalla kielellä ja/tai optimoidulla toteutuksella varmaan menisi sekuntiin ilman säikeistystäkin.

Grez [26.11.2020 13:50:18]

#

jalski kirjoitti:

Meinasin antaa säikeille omat lukuvälit työskenneltävää. Tuo Fast Doubling siis antaa säikeelle kaksi lähtöarvoa, joiden pohjalta lähteä iteratiivisesti lukuja laskemaan.

Ok, eli säikeet laskevat sitten ilmeisesti luvut muistiin ja lopuksi säie kerrallaan saavat pulauttaa tuloksen tiedostoon?

Matti Holopainen [26.11.2020 14:32:04]

#

Miten määrätään säikeille lukuvälit niin, että kaikki säikeet löytävät suurin piirtein yhtä paljon lukuja?

Grez [26.11.2020 15:26:43]

#

Uskoisin että 80 000 ensimmäistä lukua laskettaessa mentäisiin ihan järjestysluvun mukaan. Eli jos vaikka 4 säikeelle jakaisi, niin yksi yksinkertainen malli olisi:

Eli 1. - 20 000. fibonaccin luku sisältää 20 000 lukua.
20 001. - 40 000. fibonaccin luku sisältää myös 20 000 lukua.
jne.

Suurempi kysymys on että miten ne saisi tasattua niin, että jokaisella säikeellä on suunnilleen yhtä paljon laskettavaa. Tuossahan ensimmäinen joukko olisi varmasti huomattavasti nopeampi laskea kuin seuraava jne.

jalski [01.12.2020 19:55:59]

#

Alla yksinkertainen ohjelma, mikä laskee kahdeksan säikeen avulla Fibonaccin lukuja ja tallentaa tekstitiedostoihin yhteensä 80000 ensimmäistä lukua. Huomion arvoista on, että laskeminen itsessään on hyvin nopeaa ja suurin osa ajasta meneekin isojen lukujen muotoiluun ja tekstitiedostoon kirjoittamiseen.

Käytän laskemiseen "big float" tietotyyppiä "big int":n sijaan, koska niitä rajoittaa vain käytössä olevan muistin määrä, kun taas "big int" tietotyypillä on 8th:lla maksimikokonsa (Hyvin suuri sellainen kylläkin ja 8th kyllä muuntaakin sen automaattisesti "big float" tyyppiseksi tarvittaessa.).

8 constant NUMTASKS

var tasks \ Store task identifiers

10000 constant WORKSIZE  \ Number of Fibonacci numbers to calculate per thread

( WORKSIZE n:* ) 0 NUMTASKS n:1- a:generate constant work

: secs
  d:ticks d:ticks/sec n:/ ;

: bits?  \ n -- nbits-1
  n:ln 2 n:ln n:/ n:int ;

: fibo-loop
  -rot                           \ i a b
  2dup 2 n:*                     \ i a b a 2b
  over n:-                       \ i a b a (2b-a)
  n:* -rot                       \ i d=(2b-a)*a a b
  n:sqr swap n:sqr n:+           \ i d=(2b-a)*a e=b*b+a*a
  rot r@ swap n:shr 1 n:band if
    dup  \ a b b
    rot  \ b b a
    n:+  \ b c=b+a
  then ;

\ Calculate Fibonacci number using fast doubling,
\ used to calculate two starting values for the task
: fibo  \  n -- fibo(n)
  >r    \ Put n on r-stack
  F0 F1 \ a b
  ' fibo-loop 0 r@ bits? loop-
  rdrop ; \ a b

\ Task to calculate Fibonacci numbers and write results to file
: task    \ n --
  dup fibo a:new 2 pick a:push
  over a:push -rot
  ( dup rot n:+ rot over a:push -rot ) WORKSIZE 2 n:- times 2drop
  swap >s ".txt" s:+ f:create swap
  ( "%.f\n" s:strfmt f:write drop ) a:each! drop f:close ;

\ Start tasks, passing the start value of work as parameter
: start-tasks
  a:new
  ( work a:shift nip 1 ' task t:task-n a:push ) NUMTASKS times
  tasks ! ;

\ Wait for tasks to finish
: wait-tasks
  tasks @ t:wait ;

: app:main
  16720 n#  \ Set the accurary, e.g. the number of significant digits
  "Calculating Fibonacci numbers...\n" .
  secs
  start-tasks
  wait-tasks
  secs swap n:- "Total time: %.3f seconds\n" s:strfmt .
  bye ;

Grez [01.12.2020 21:59:19]

#

26 sekuntia näytti kestävän AMD Ryzen™ 7 4700U:lla (eli 8-ytimisellä budjettiprossulla) Eli käytännössä lienee niin että lukujen 70001. - 80000. laskemiseen ja tulostamiseen menee tuolla se 26 sekuntia. Hieman paremman suorituskyvyn varmaankin saisi, jos säikeet laskisi
1. luvut 1-25000
2. luvut 25001-42675
3. luvut 42676-55172
4. luvut 55173-64007
5. luvut 64008-70254
6. luvut 70255-74671
7. luvut 74672-77793
8. luvut 77794-80000

Tai jotain tähän tyyliin.

Edit: Korjattu että tarkoitin laskemista ja tulostamista

jalski [01.12.2020 22:40:08]

#

Grez kirjoitti:

(01.12.2020 21:59:19): 26 sekuntia näytti kestävän AMD Ryzen™ 7...

Laskentaan menee omalla yli 10 vuotta vanhalla tietokoneellani kokonaisuudessaan yhteensä n. 350 millisekuntia ja loput, eli melkein kaikki työhön menevä aika näyttäisi menevän lukujen muuntamiseen ja tulostamiseen.

Olet oikeassa, että säikeiden kuorma ei toteutuksessani ole kauhean tasainen ja optimaalinen koska viimeisten säikeiden käsittelemät luvut ovat ensimmäisiin säikeisiin verrattuna aivan eri suuruusluokkaa.

Grez [02.12.2020 08:50:14]

#

Kiinnostaisi tietää montako säiettä se 10 vuotta vanha tietokoneesi pystyy suorittamaan samanaikaisesti? Eli esim. 1 coren CPU hyperthreadingilla pystyisi suorittamaan 2 säiettä jossain määrin samanaikaisesti. Tämän tyyppisessä tehtävässä ei pitäisi tulla merkittävää hyötyä useammasta säikeestä, kuin mitä prosessori oikeasti pystyy suorittamaan samanaikaisesti.

jalski [02.12.2020 09:05:04]

#

Grez kirjoitti:

Kiinnostaisi tietää montako säiettä se 10 vuotta vanha tietokoneesi pystyy suorittamaan samanaikaisesti?

Tuossa taitaa olla kolme hyper-threadingilla varustettua ydintä.


Sivun alkuun

Vastaus

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

Tietoa sivustosta