Uuden äärellä rinnakkaisuus ja samanaikaisuus

Samankaltaiset tiedostot
Uuden äärellä ohjelmoitava vai oppiva kone?

Abstraktiot ja analyysi algoritmit ja informaation esitykset

Uuden äärellä ohjelmoitava vai oppiva kone?

ICS-A1120 Ohjelmointi 2 (5 op, IV V, kevät 2015)

Tietokoneen mysteeri ohjelmoitava kone

ICS-A1120 Ohjelmointi 2 (5 op, IV V, kevät 2016)

Ongelma(t): Mihin perustuu tietokoneiden suorituskyky ja sen jatkuva kasvu? Mitkä tekijät rajoittavat suorituskyvyn parantamista ja mitkä niistä ovat

Tietokoneen mysteeri ohjelmoitava kone

Ongelma(t): Mihin perustuu tietokoneiden suorituskyky ja sen jatkuva kasvu? Mitkä tekijät rajoittavat suorituskyvyn parantamista ja mitkä niistä ovat

CS-A1120 Ohjelmointi 2 (5 op, IV V, kevät 2018)

Rinnakkaisuus. parallel tietokoneissa rinnakkaisia laskentayksiköitä concurrent asioita tapahtuu yhtaikaa. TTY Ohjelmistotekniikka

RINNAKKAINEN OHJELMOINTI A,

Rinnakkaistietokoneet luento S

Rinnakkaisuuden hyväksikäyttö peleissä. Paula Kemppi

Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo

Capacity Utilization

Intel Threading Building Blocks

Graafisen käyttöliittymän ohjelmointi Syksy 2013

On instrument costs in decentralized macroeconomic decision making (Helsingin Kauppakorkeakoulun julkaisuja ; D-31)

anna minun kertoa let me tell you

On instrument costs in decentralized macroeconomic decision making (Helsingin Kauppakorkeakoulun julkaisuja ; D-31)

CUDA. Moniydinohjelmointi Mikko Honkonen

On instrument costs in decentralized macroeconomic decision making (Helsingin Kauppakorkeakoulun julkaisuja ; D-31)

C++11 seminaari, kevät Johannes Koskinen

Ohjelmoinnin peruskurssien laaja oppimäärä

Efficiency change over time

Ohjelmoinnin peruskurssien laaja oppimäärä

1.3Lohkorakenne muodostetaan käyttämällä a) puolipistettä b) aaltosulkeita c) BEGIN ja END lausekkeita d) sisennystä

1. Liikkuvat määreet

Choose Finland-Helsinki Valitse Finland-Helsinki

Oppimistavoitteet kurssilla Rinnakkaisohjelmointi

812336A C++ -kielen perusteet,

1.3 Lohkorakenne muodostetaan käyttämällä a) puolipistettä b) aaltosulkeita c) BEGIN ja END lausekkeita d) sisennystä

1. SIT. The handler and dog stop with the dog sitting at heel. When the dog is sitting, the handler cues the dog to heel forward.

FinFamily PostgreSQL installation ( ) FinFamily PostgreSQL

Opiskelijat valtaan! TOPIC MASTER menetelmä lukion englannin opetuksessa. Tuija Kae, englannin kielen lehtori Sotungin lukio ja etälukio

Algoritmit 1. Luento 1 Ti Timo Männikkö

Verkon värittämistä hajautetuilla algoritmeilla

21~--~--~r--1~~--~--~~r--1~

Information on Finnish Language Courses Spring Semester 2017 Jenni Laine

11/20: Konepelti auki

TW-LTE 4G/3G. USB-modeemi (USB 2.0)

Results on the new polydrug use questions in the Finnish TDI data

Uusi Ajatus Löytyy Luonnosta 4 (käsikirja) (Finnish Edition)

Salasanan vaihto uuteen / How to change password

Käyttöliittymät II. Käyttöliittymät I Kertaus peruskurssilta. Keskeisin kälikurssilla opittu asia?

Vertaispalaute. Vertaispalaute, /9

Käyttöjärjestelmät: poissulkeminen ja synkronointi

HARJOITUS- PAKETTI A

CALL TO ACTION! Jos aamiaistilaisuudessa esillä olleet aiheet kiinnostavat syvemminkin niin klikkaa alta lisää ja pyydä käymään!

AYYE 9/ HOUSING POLICY

Exercise 1. (session: )

Rinnakkaisuus. Tarkastelemme, miten algoritmien suoritusta voi nopeuttaa käyttämällä useaa laskentayksikköä samanaikaisesti.

EUROOPAN PARLAMENTTI

Tietorakenteet ja algoritmit

10. Painotetut graafit

2_1----~--~r--1.~--~--~--,.~~

Sisällysluettelo Table of contents

812315A Ohjelmiston rakentaminen. Asynkronisuus

Rinnakkaisuuden hyväksikäyttö tietokonepeleissä

Rinnakkaisohjelmointi kurssi. Opintopiiri työskentelyn raportti

You can check above like this: Start->Control Panel->Programs->find if Microsoft Lync or Microsoft Lync Attendeed is listed

The CCR Model and Production Correspondence

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

VUOSI 2015 / YEAR 2015

Green Growth Sessio - Millaisilla kansainvälistymismalleilla kasvumarkkinoille?

Information on Finnish Language Courses Spring Semester 2018 Päivi Paukku & Jenni Laine Centre for Language and Communication Studies

Algoritmit 1. Luento 3 Ti Timo Männikkö

Rekisteröiminen - FAQ

Ajettavat luokat: SM: S1 (25 aika-ajon nopeinta)

Tutoriaaliläsnäoloista

FIS IMATRAN KYLPYLÄHIIHDOT Team captains meeting

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Rinnakkaistietokoneet luento S

Hankkeiden vaikuttavuus: Työkaluja hankesuunnittelun tueksi

Ohjelmoinnin peruskurssien laaja oppimäärä

Tilanne sekä MS-A0003/4* Matriisilaskenta 5 op

Numeriikan kirjastoja

1. Olio-ohjelmointi 1.1

Ohjelmoinnin peruskurssien laaja oppimäärä

A ja B pelaavat sarjan pelejä. Sarjan voittaja on se, joka ensin voittaa n peliä.

Palvelut. Sulautetut järjestelmät Luku 2 Sivu 1 (??) Sulautetut käyttöjärjestelmät

Rinnakkaistietokoneet luento S

Network to Get Work. Tehtäviä opiskelijoille Assignments for students.

Algoritmit 1. Demot Timo Männikkö

Rinnakkaistietokoneet luento S

Hajautettujen sovellusten muodostamistekniikat, TKO_2014 Johdatus kurssiin

Laundry Center. Radiotaajuuslinkki pesukoneen ja kuivausrummun välillä

ITKP102 Ohjelmointi 1 (6 op)

ATLAS-kartan esittely - Peli palveluiden yhteiskehittämisen menetelmistä Päivi Pöyry-Lassila, Aalto-yliopisto

SIMULINK S-funktiot. SIMULINK S-funktiot

Algoritmit 1. Luento 2 Ke Timo Männikkö

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Harjoitus 6. Käytä String-luokasta vain charat- ja length-operaatioita.

Information on Finnish Courses Autumn Semester 2017 Jenni Laine & Päivi Paukku Centre for Language and Communication Studies

TIES530 TIES530. Moniprosessorijärjestelmät. Moniprosessorijärjestelmät. Miksi moniprosessorijärjestelmä?

Constructive Alignment in Specialisation Studies in Industrial Pharmacy in Finland

Tampereen kansainvälinen koulu, FISTA

Voice Over LTE (VoLTE) By Miikka Poikselkä;Harri Holma;Jukka Hongisto

Information on preparing Presentation

Transkriptio:

01110111010110 11110101010101 00101011010011 01010111010101 01001010101010 10101010101010 Uuden äärellä rinnakkaisuus ja samanaikaisuus Petteri Kaski Tietotekniikan laitos Aalto-yliopisto ICS-A1120 Ohjelmointi 2 20. huhtikuuta 2016 10101011110101 01010101011101 01010111010110 10101101010110 10101110101010 11101010101101 01110111010110 10111011010101 11110101010101 00010101010101 01011010101110

What s in your hands, I think and hope, is intelligence: the ability to see the machine as more than when you were first led up to it, that you can make it more. Alan J. Perlis (1 April 1922 7 February 1990) in foreword to Harold Abelson and Gerald Jay Sussman, with Julie Sussman, Structure and Interpretation of Computer Programs, 2nd ed., MIT Press, 1996

Sisältö (kierrokset ja moduulit) I 1. Lämmittelykierros Tietokoneen mysteeri 2. Bitit ja data 3. Kombinaatiologiikka 4. Sekventiaalilogiikka 5. Ohjelmoitava kone II Abstraktiot ja analyysi 6. 7. 8. 9. III Funktionaalinen ohjelmointityyli Suorituskykyanalyysi Rekursio Algoritmit ja informaation esitykset Uuden äärellä 10. Rinnakkaisuus ja samanaikaisuus 11. Virtualisointi ja skaalautuvuus 12. Ohjelmoitava vai oppiva kone? (Intel Xeon Phi Knights Landing -lastu, 72 suoritusydintä, 8 miljardia transistoria, 14 nanometrin litografia) (Google Hamina)

Uuden äärellä 10. Samanaikaisuus ja rinnakkaisuus 11. Virtualisointi ja skaalautuvuus 12. Ohjelmoitava vai oppiva kone? helppoa rinnakkaisuutta: rinnakkaiskokoelmat, futuurit ja lupaukset laskentaa saa kuin sähköä seinästä skaalautuvuus omasta koneesta tehdashallitietokoneeseen*; Apache Spark (asennettu Maarille) konetta voi joskus opettaa sen sijaan että sitä ohjelmoidaan, ts. kone osaa joskus näppärästi yleistää sille esitetyistä esimerkeistä *) vain sähkösopimus (ja luottokortti) tarvitaan

The complexity for minimum component costs has increased at a rate of roughly a factor of two per year. Certainly over the short term this rate can be expected to continue, if not to increase. Over the longer term, the rate of increase is a bit more uncertain, although there is no reason to believe it will not remain nearly constant for at least 10 years. Gordon E. Moore, in Electronics (19 April 1965) http://en.wikipedia.org/wiki/moore's_law

http://spectrum.ieee.org/static/special-report-50-years-of-moores-law

The end of dramatic exponential growth in single-processor performance marks the end of the dominance of the single microprocessor in computing. The era of sequential computing must give way to a new era in which parallelism is at the forefront. The Future of Computing Performance: Game Over or Next Level? Samuel H. Fuller and Lynette I. Millett, Eds.; Committee on Sustaining Growth in Computing Performance; National Research Council, The National Academies Press, 2011 10. Samanaikaisuus ja rinnakkaisuus

Intel Haswell (***) 22 nm Haswell AVX2 suoritusydin: 2 x 8 = 16 liukulukulaskentayksikköä (binary32) Intel Xeon E5-2690 v3 12 x AVX2 suoritusydintä = 192 liukulukulaskentayksikköä (2.6 GHz base, 3.5 GHz boost) [1.0 Tflops base, 1.3 Tflops boost] http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf

NVIDIA Kepler (***) SMX: 6 x 32 = 192 liukulukulaskentayksikköä (binary32) Tesla K40 Kepler GK110 (15 SMX) = 2880 liukulukulaskentayksikköä (745 MHz base, 875 MHz boost) [4.3 Tflops base, 5.0 Tflops boost] Tesla K80 2 x Kepler GK210 (13 SMX) = 2 x 2496 = 4992 liukulukulaskentayks. (562 MHz base, 875 MHz boost) [5.6 Tflops base, 8.7 Tflops boost] 28 nm http://international.download.nvidia.com/pdf/kepler/nvidia-kepler-gk110-gk210-architecture-whitepaper.pdf

10. Samanaikaisuus ja rinnakkaisuus

Peräkkäismalli Tähän asti kurssilla olemme rajoittuneet tarkastelemaan laskentaa yksinkertaistettuna ilmiönä jossa ei ole samanaikaisuutta eikä rinnakkaisuutta (~ [yksi] prosessori[ydin] suorittaa konekielikäskyjä yksi kerrallaan, peräkkäin, eristettynä muusta maailmasta)

Laskenta todellisuudessa Peräkkäisyys on sangen karkea yksinkertaistus vallitsevasta todellisuudesta Laskenta on käytännössä samanaikaisesti ja rinnakkain etenevä ilmiö, yli mittakaavojen yksittäisen prosessorin suoritusytimistä tehdashallitietokoneisiin ja tietoverkkoihin (~ kaikki Internetiin kytketyt tietokoneet)

Samanaikaisuus ja rinnakkaisuus ohjelmoijan arjessa? Rinnakkaisnopeutus. Eräät tehtävät ovat luonteeltaan rinnakkaistuvia, ts. koko tehtävä, tai eräät tehtävän osat voidaan pilkkoa toisistaan riippumattomiin alitehtäviin Riippumattomat alitehtävät voidaan suorittaa rinnakkain siten, että koko tehtävä valmistuu ( seinäkelloajassa ) nopeammin kuin peräkkäin suoritettuna Valkopyykki ja väripyykki, kaksi pesukonetta

Samanaikaisuus ja rinnakkaisuus ohjelmoijan arjessa? Kokonaissuorituskyky. Monet yksittäiset tehtävät/palvelupyynnöt eivät rinnakkaistu, koska niiden suoritusaskelten välillä on riippuvuuksia (edellisen askelen tulos on oltava käytettävissä jotta seuraava voidaan suorittaa) Riippuvuuksista huolimatta rinnakkaisuudella voidaan usein parantaa kokonaissuorituskykyä (ratkaistuja tehtäviä tai palvelupyyntöjä aikayksikköä kohti), olettaen että tehtävät/pyynnöt ovat toisistaan riippumattomia Viisi pesulaa pesee viisinkertaisen määrän paitoja, vaikka mikään yksittäinen paita (esipesu, pesu, linkous, silitys) ei sen nopeammin tule pestyksi

Samanaikaisuus ja rinnakkaisuus ohjelmoijan arjessa? Saatavuus/vasteaika. Rinnakkaisuus mahdollistaa alitehtävien jakamisen ja aikatauluttamisen siten, että järjestelmän eri osat ovat saatavilla halutulla tavalla Pyykin vastaanotto/pestyjen luovutus toimii välittömästi riippumatta siitä mitä varsinaisella pesulinjalla tapahtuu

Samanaikaisuus ja rinnakkaisuus ohjelmoijan arjessa? Käytännössä laskenta on ilmiö, jossa useat samanaikaiset suoritukset etenevät rinnakkain, mutta vuorovaikutuksessa Mitä vähemmän vuorovaikutusta, sitä helpompaa on hyödyntää rinnakkaisuutta

10. Samanaikaisuus ja helppo rinnakkaisuus Säikeet ja rinnakkaisuus Asynkroninen suoritus, synkronointi Riippuvuus ja riippumattomuus suorituksessa...... yksinkertaisessa funktionaalisessa suoritusmallissa riippuvuuksien DAG-mallinnus Nopeutuskerroin ja Amdahlin laki Helppo rinnakkaisuus ohjelmoinnissa: Rinnakkaiskokoelmat Futuurit ja lupaukset

Suoritussäie (thread of execution) Ohjelmassa käytössä vain yksi suoritussäie (kuten kurssilla tähän asti) = peräkkäisohjelma

Monisäikeinen ohjelma

Monisäikeinen ohjelma

Monisäikeinen ohjelma Jokaisella säikeellä on oma sisäinen tila (ohjelmalaskuri, muut rekisterit, välimuisti,...) Lisäksi säikeillä on jaettu tila (ohjelman keskusmuistialue, käyttöjärjestelmäresurssit,...) Jokaisella säikeellä on oma näkymä jaettuun tilaan näkymä riippuu säikeen sisäisestä tilasta (esimerkiksi säikeen oman välimuistin sisällöstä!) ja säikeiden vuorovaikutuksesta

Asynkronisuus ja synkronointi Kunkin säikeen suoritus etenee asynkronisesti ja omassa näkymässään, oleellisesti täysin riippumatta mitä muut säikeet tekevät, ellei ohjelmoija erikseen synkronoi säikeiden suoritusta

Vaikea rinnakkaisuus käsittelee säikeitä manuaalisesti erikseen synkronoi säikeitä keskenään yrittää välttää synkronointia käymällä läpi kaikki tavat miten säikeet voivat vuorovaikuttaa keskenään ja synkronoimalla säikeiden toimintaa mahdollisimman vähän siten, että haluttu säikeiden yhteistoiminta saadaan kuitenkin aikaan vaikeaa

Helppo rinnakkaisuus käyttää korkean tason abstraktioita (esimerkiksi rinnakkaiskokoelmat, futuurit ja lupaukset) ohjelmoija ei ota kantaa säikeistykseen eikä synkronointiin ohjelmoija kertoo mitä saa rinnakkaistaa, mutta ei kerro miten rinnakkaistetaan ohjelmoijan vastuulla on edelleen ymmärtää, että korkean tason abstraktiot pohjautuvat säikeisiin ja synkronointiin jaetusta tilasta ja säikeiden omista näkymistä ei tule olettaa mitään mitä abstraktio ei lupaa taata

[Lyhyesti vaikeasta rinnakkaisuudesta ]

Säikeet ja asynkronisuus Scalalla class SumWorker(name: String, target: Int, report: Int) extends Runnable { def run() { // This method contains the code we want to run in a thread //... say, we want to take the sum of the integers i = 1,2,...,target, // and issue a progress report to the console after every // "report" integers var s = 0L var i = 1 while(i <= target) { s = s + i if(i % report == 0) { println("worker %s reporting at i = %d".format(name, i)) } i = i + 1 } println("worker %s is done and reports sum s = %d".format(name, s)) } } // The thread stops when run() returns, i.e. here

Valmistellaan kolme työtä ja kolme säeittä ajamaan töitä... val wa = new SumWorker("SumA", 100000000, 10000000) // prepare worker A val wb = new SumWorker("SumB", 100000000, 10000000) // prepare worker B val wc = new SumWorker("SumC", 100000000, 10000000) // prepare worker C val ta = new Thread(wa) val tb = new Thread(wb) val tc = new Thread(wc) // prepare a thread for worker A // prepare a thread for worker B // prepare a thread for worker C... ja käynnistetään säikeet ta.start() tb.start() tc.start() // start thread of worker A // start thread of worker B // start thread of worker C

Säikeet töissä, asynkronisesti... [demo]

Asynkroninen suoritus vieläkin selkeämmin... class Greeter(id: Int, num: Int) extends Runnable { def run() { for(i <- 0 until num) { print("%d".format(id)) // greet by writing a single digit to console } } } val n = 10 // ten greeters -- one for each digit 0,1,...,9 val num_greetings = 200 // each greeter sends 200 greetings val greeters = (0 until n).map(id => new Greeter(id, num_greetings)) val threads = greeters.map(new Thread(_)) threads.foreach(_.start()) [demo]

Asynkronisuus ja synkronointi Kunkin säikeen suoritus etenee asynkronisesti ja omassa näkymässään, oleellisesti täysin riippumatta mitä muut säikeet tekevät, ellei ohjelmoija erikseen synkronoi säikeiden suoritusta

Kaksi esimerkkiä synkronoinnista Säikeen käynnistäminen käynnistävän ja käynnistyvän t.start() säikeen näkymä jaettuun tilaan on sama, käynnistymishetkellä Säikeen pysähtymisen odottaminen ( säikeeseen liittyminen, join ) t.join() pysähtyneen säikeen jaettuun tilaan tekemät muutokset näkyvät sitä odottaneen säikeen näkymässä

Ohjelmoijan vastuu odottelusta Ohjelmoijan vastuulla on huolehtia, että odottaminen päättyy (Ei ole mitenkään vaikeaa epähuomiossa saada kahta säiettä odottamaan toistensa touhuja ristiin!)

Ohjelmoijan vastuu säikeiden näkymistä jaettuun tilaan

[Lyhyesti vaikeasta rinnakkaisuudesta ] Samanaikaisuuden ja rinnakkaisuuden hallinta on kokeneellekin ohjelmoijalle vaikea asia Varovaisuus on siis paikallaan, ja lisäoppi on suotavaa, mutta kauhusta ei pidä kangistua

Helppo rinnakkaisuus

Rinnakkaiskokoelmat http://docs.scala-lang.org/overviews/parallel-collections/overview.html Lisää vain.par kokoelmatyypin perään tämän jälkeen kaikki kokoelmalle tehtävä laskenta suoritetaan automaattisesti rinnakkain [demo: matriisikertolasku rinnakkain, isoille matriiseille] (Huom: Lisäämällä.par otat ohjelmoijana vastuun, että kutakin kokoelman alkiota käsittelevä laskenta kaikkine sivuvaikutuksineen on riippumatonta muita kokoelman alkioita käsittelevästä laskennasta)

Mitä voidaan rinnakkaistaa?

Funktionaalinen, yksinkertaistettu suoritusmalli Laskenta on jono arvoja, jotka saadaan soveltamalla puhtaita funktioita jonon edeltäviin arvoihin

Riippumattomuus

... koska arvot ovat toisistaan riippumattomia, laskenta voidaan rinnakkaistaa...

Esimerkki nk. SIMD-rinnakkaisuudesta ( Single Instruction, Multiple Data )

Riippuvuus

Mitä voidaan rinnakkaistaa? Riippumaton voidaan rinnakkaistaa, riippuvaista ei voida

Riippuvuuksien kartoittaminen (yksinkertaistetussa funktionaalisessa mallissa)

Riippuvuudet suunnattuna asyklisenä graafina ( DAG ) Oikealla oleva graafi esittää samaa laskentaa (funktioiden parametrien järjestystä vaille)

Sama graafi, oikealla piirrettynä yksi topologinen taso kerrallaan

Idea: Suoritetaan laskenta ahneesti ( opportunistisesti ), ts. jokaisen funktion laskenta aloitetaan heti ja riippumatta muusta käynnissä olevasta laskennasta (*) kun funktiolle annettavat arvot ovat käytettävissä *) mikäli käytännössä rinnakkaislaskentaresurssit vain riittävät!

Liitetään jokaiseen funktiokutsuun 3 2 4 sen suoritusaika Havainto (*): 1 1 5 Ahneella suorituksella 5 1 kokonaislaskenta-aika on laskentagraafin ajaltaan pisimmän suunnatun polun ( kriittisen polun **) vaatima laskenta-aika *) rajattomilla rinnakkaisresursseilla ja vailla viiveitä säikeitä käynnistettäessä, liitettäessä jne **) joita voi olla useita

Futuurit ja lupaukset http://docs.scala-lang.org/overviews/core/futures.html Futuuri ( Future ) (olio, joka edustaa arvoa tai virhettä tulevaisuudessa kun futuuriin liittyvä laskenta valmistuu) Futuureilla on mahdollista laskea ennakkoon (vrt. ennen kuin arvot valmistuvat, on jo kerrottu mitä niille tehdään ) Lupaus ( Promise ) lupausabstraktio ( erikseen täydennettävä futuuri )

Futuurit ja lupaukset [demo]

Helppo rinnakkaisuus http://docs.scala-lang.org/overviews/parallel-collections/overview.html http://docs.scala-lang.org/overviews/core/futures.html

Tehtävät parallel matriisikertolaskun rinnakkaistaminen rinnakkaiskokoelmilla (lisää vain.par ja johan nopeutuu, isoille matriiseille) futures futuuriabstraktio ( arvo tai virhe tulevaisuudessa kun asynkroninen suoritus valmistuu) futuureilla laskenta ennakkoon (vrt. ennen kuin arvot valmistuvat on jo kerrottu mitä niille tehdään ) mekaaninen funktioiden nostaminen tulevaisuuteen ( future-lifting / futurization ) ja tästä saatava tehokkuusetu promises lupausabstraktio ( erikseen täydennettävä futuuri ) työskentely futuureilla ja lupauksilla, lupausten täydentäminen futuureilla threads (haastetehtävä) säikeiden kanssa työskentelyä

Rinnakkaisnopeutuskerroin ja Amdahlin laki suoritusaika peräkkäissuorituksena suoritusaika rinnakkaisuutta hyväksikäyttäen

Amdahlin laki Peräkkäissuorituksen osuus muuten täysin rinnakkaistuvasta ongelmasta = 1 P, missä 0 P < 1 Rinnakkaisnopeutuskerroin on enintään 1/(1 P) http://en.wikipedia.org/wiki/amdahl's_law