• Keine Ergebnisse gefunden

Praktische Informatik 3: Funktionale Programmierung Vorlesung 12 vom 15.01.2013: Effizienzaspekte

N/A
N/A
Protected

Academic year: 2022

Aktie "Praktische Informatik 3: Funktionale Programmierung Vorlesung 12 vom 15.01.2013: Effizienzaspekte"

Copied!
38
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Praktische Informatik 3: Funktionale Programmierung Vorlesung 12 vom 15.01.2013: Effizienzaspekte

Christoph Lüth

Universität Bremen

Wintersemester 2012/13

(2)

Fahrplan

I Teil I: Funktionale Programmierung im Kleinen

I Teil II: Funktionale Programmierung im Großen

I Teil III: Funktionale Programmierung im richtigen Leben

I Effizienzaspekte

I Eine Einführung in Scala

I Rückblich & Ausblick

(3)

Inhalt

I Zeitbedarf:Endrekursion — whilein Haskell

I Platzbedarf:Speicherlecks

I “Unendliche” Datenstrukturen

I Verschiedene anderePerformancefallen:

I ÜberladeneFunktionen,Listen

I “Usual Disclaimers Apply”:

I Erste Lösung: bessereAlgorithmen

I Zweite Lösung:Büchereien nutzen

(4)

Inhalt

I Zeitbedarf:Endrekursion — whilein Haskell

I Platzbedarf:Speicherlecks

I “Unendliche” Datenstrukturen

I Verschiedene anderePerformancefallen:

I ÜberladeneFunktionen,Listen

I “Usual Disclaimers Apply”:

I Erste Lösung: bessereAlgorithmen

I Zweite Lösung:Büchereien nutzen

(5)

Effizienzaspekte

I ZurVerbesserungder Effizienz:

I Analyse derAuswertungsstrategie

I . . . und desSpeichermanagement

I Der ewige Konflikt:Geschwindigkeitvs. Platz

I Effizenzverbesserungen durch

I Endrekursion: Iteration in funktionalen Sprachen

I Striktheit:Speicherlecksvermeiden (bei verzögerter Auswertung)

I Vorteil: Effizienzmuss nicht im Vordergrund stehen

(6)

Effizienzaspekte

I ZurVerbesserungder Effizienz:

I Analyse derAuswertungsstrategie

I . . . und desSpeichermanagement

I Der ewige Konflikt:Geschwindigkeitvs. Platz

I Effizenzverbesserungen durch

I Endrekursion: Iteration in funktionalen Sprachen

I Striktheit:Speicherlecksvermeiden (bei verzögerter Auswertung)

I Vorteil: Effizienzmuss nicht im Vordergrund stehen

(7)

Endrekursion

Definition (Endrekursion)

Eine Funktion istendrekursiv, wenn

(i) es genaueinen rekursiven Aufruf gibt,

(ii) dernicht innerhalb einesgeschachtelten Ausdrucks steht.

I D.h. darübernur Fallunterscheidungen:caseoder if

I Entsprichtgotooderwhile in imperativen Sprachen.

I Wird inSprungoder Schleifeübersetzt.

I Brauchtkeinen Platzauf dem Stack.

(8)

Einfaches Beispiel

I In Haskell:

e v e n x = i f x< 1 then x == 0 e l s e e v e n ( x−2)

I Übersetzt nach C:

i n t e v e n (i n t x )

{ i f ( x<1 ) r e t u r n x == 0 ; e l s e r e t u r n ( e v e n ( x−2 ) ) ; }

I Äquivalente Formulierung:

i n t e v e n (i n t x )

{ i f ( x< 1 ) r e t u r n x == 0 ;

e l s e { x −= 2 ; r e t u r n e v e n ( x ) ; } }

I Iterative Variante mit Schleife:

i n t e v e n (i n t x )

{ w h i l e ( ! ( x<1 ) ) x −= 2 ; r e t u r n x == 0 ; }

(9)

Beispiel: Fakultät

I fac1 nicht endrekursiv:

f a c 1 :: I n t e g e r→ I n t e g e r

f a c 1 n = i f n == 0 then 1 e l s e n ∗ f a c 1 ( n−1)

I fac2 endrekursiv:

f a c 2 :: I n t e g e r→ I n t e g e r

f a c 2 n = f a c ’ n 1 where

f a c ’ :: I n t e g e r→ I n t e g e r→ I n t e g e r f a c ’ n a c c = i f n == 0 then a c c

e l s e f a c ’ ( n−1) ( n∗a c c )

I fac1 verbraucht Stack, fac2 nicht.

(10)

Beispiel: Fakultät

I fac1 nicht endrekursiv:

f a c 1 :: I n t e g e r→ I n t e g e r

f a c 1 n = i f n == 0 then 1 e l s e n ∗ f a c 1 ( n−1)

I fac2 endrekursiv:

f a c 2 :: I n t e g e r→ I n t e g e r

f a c 2 n = f a c ’ n 1 where

f a c ’ :: I n t e g e r→ I n t e g e r→ I n t e g e r f a c ’ n a c c = i f n == 0 then a c c

e l s e f a c ’ ( n−1) ( n∗a c c )

I fac1 verbraucht Stack, fac2 nicht.

(11)

Beispiel: Listen umdrehen

I Liste umdrehen,nicht endrekursiv:

r e v ’ :: [ a ]→ [ a ]

r e v ’ [ ] = [ ]

r e v ’ ( x : x s ) = r e v ’ x s ++ [ x ]

I Hängt auch nochhintenan —O(n2)!

I Liste umdrehen,endrekursivund O(n):

r e v :: [ a ]→ [ a ]

r e v x s = r e v 0 x s [ ] where r e v 0 [ ] y s = y s

r e v 0 ( x : x s ) y s = r e v 0 x s ( x : y s )

I Beispiel: last (rev [1..10000])

(12)

Beispiel: Listen umdrehen

I Liste umdrehen,nicht endrekursiv:

r e v ’ :: [ a ]→ [ a ]

r e v ’ [ ] = [ ]

r e v ’ ( x : x s ) = r e v ’ x s ++ [ x ]

I Hängt auch nochhintenan —O(n2)!

I Liste umdrehen,endrekursivund O(n):

r e v :: [ a ]→ [ a ]

r e v x s = r e v 0 x s [ ] where r e v 0 [ ] y s = y s

r e v 0 ( x : x s ) y s = r e v 0 x s ( x : y s )

I Beispiel: last (rev [1..10000])

(13)

Überführung in Endrekursion

I Gegeben Funktion f0:ST

f0 x = if B x then H x

elseφ(f0 (K x)) (E x)

I MitK :S S,φ:T T T,E :ST, H:S T.

I Voraussetzung:φassoziativ, e:T neutrales Element

I Dann istendrekursive Form:

f :ST

f x = g x e where

g x y = if B x then φ(H x) y

elseg (K x) (φ(E x) y)

(14)

Beispiel

I Länge einer Liste (nicht-endrekursiv) l e n g t h ’ :: [ a ]→ I n t

l e n g t h ’ x s = i f n u l l x s then 0

e l s e 1+ l e n g t h ’ ( t a i l x s )

I Zuordnung der Variablen:

K(x) 7→ tailx E(x) 7→ 1 φ(x,y) 7→ x+y

B(x) 7→ nullx H(x) 7→ 0

e 7→ 0

I Es gilt:φ(x,e) =x+0=x (0 neutrales Element)

(15)

Beispiel

I Damitendrekursive Variante:

l e n g t h :: [ a ]→ I n t

l e n g t h x s = l e n x s 0 where

l e n x s y = i f n u l l x s then y −−was: y+ 0 e l s e l e n ( t a i l x s ) ( 1+ y )

I AllgemeinesMuster:

I Monoid(φ,e): φassoziativ,eneutrales Element.

I Zusätzlicher Parameterakkumuliert Resultat.

(16)

Endrekursive Aktionen

I Nicht endrekursiv:

g e t L i n e s ’ :: IO S t r i n g

g e t L i n e s ’ = do s t r← g e t L i n e

i f n u l l s t r then r e t u r n " "

e l s e do r e s t← g e t L i n e s ’ r e t u r n ( s t r++ r e s t )

I Endrekursiv:

g e t L i n e s :: IO S t r i n g g e t L i n e s = g e t i t " " where

g e t i t r e s = do s t r← g e t L i n e

i f n u l l s t r then r e t u r n r e s e l s e g e t i t ( r e s++ s t r )

(17)

Fortgeschrittene Endrekursion

I Akkumulationvon Ergebniswerten durch closures

I closure: partiell applizierte Funktion

I Beispiel: die KlasseShow

I Nur Methodeshow wäre zu langsam (O(n2)):

c l a s s Show a where show :: a S t r i n g

I Deshalb zusätzlich

s h o w s P r e c :: I n t a S t r i n g S t r i n g show x = s h o w s P r e c 0 x " "

I String wird erst aufgebaut, wenn er ausgewertet wird (O(n)).

(18)

Fortgeschrittene Endrekursion

I Akkumulationvon Ergebniswerten durch closures

I closure: partiell applizierte Funktion

I Beispiel: die KlasseShow

I Nur Methodeshow wäre zu langsam (O(n2)):

c l a s s Show a where show :: a S t r i n g

I Deshalb zusätzlich

s h o w s P r e c :: I n t a S t r i n g S t r i n g show x = s h o w s P r e c 0 x " "

I String wird erst aufgebaut, wenn er ausgewertet wird (O(n)).

(19)

Beispiel: Mengen als Listen

data S e t a = S e t [ a ] Zu langsamwäre

i n s t a n c e Show a⇒ Show ( S e t a ) where show ( S e t e l e m s ) =

" { " ++ i n t e r c a l a t e " , " ( map show e l e m s ) ++ " } "

Deshalb besser

i n s t a n c e Show a⇒ Show ( S e t a ) where

s h o w s P r e c i ( S e t e l e m s ) = showElems e l e m s where showElems [ ] = ( " {} " ++)

showElems ( x : x s ) = ( ’ { ’ : ) ◦ s h o w s x ◦ s h o w l x s where s h o w l [ ] = ( ’ } ’ : )

s h o w l ( x : x s ) = ( ’ , ’ : ) ◦ s h o w s x ◦ s h o w l x s

(20)

Speicherlecks

I Garbage collectiongibt unbenutztenSpeicher wieder frei.

I Unbenutzt: Bezeichner nicht mehr imerreichbar

I Verzögerte Auswertungeffizient, weil nur bei Bedarfausgewertet wird

I Aber Achtung:Speicherlecks!

I Eine Funktion hat einSpeicherleck, wenn Speicher unnötiglange im Zugriff bleibt.

I “Echte” Speicherlecks wie in C/C++nicht möglich.

I Beispiel: getLines, fac2

I Zwischenergebnisse werdennicht auswertet.

I Insbesondere ärgerlich beinicht-terminierenden Funktionen.

(21)

Speicherlecks

I Garbage collectiongibt unbenutztenSpeicher wieder frei.

I Unbenutzt: Bezeichner nicht mehr imerreichbar

I Verzögerte Auswertungeffizient, weil nur bei Bedarfausgewertet wird

I Aber Achtung:Speicherlecks!

I Eine Funktion hat einSpeicherleck, wenn Speicher unnötiglange im Zugriff bleibt.

I “Echte” Speicherlecks wie in C/C++nicht möglich.

I Beispiel: getLines, fac2

I Zwischenergebnisse werdennicht auswertet.

I Insbesondere ärgerlich beinicht-terminierenden Funktionen.

(22)

Striktheit

I Strikte Argumenteerlauben Auswertung vorAufruf

I DadurchkonstanterPlatz beiEndrekursion.

I ErzwungeneStriktheit:seq :: α→β→β

⊥‘seq‘ b = ⊥ a ‘seq‘ b = b

I seqvordefiniert (nichtinHaskell definierbar)

I ($!) :: (a→b)→a→bstrikte Funktionsanwendung f $ ! x = x ‘ s e q ‘ f x

I ghc machtStriktheitsanalyse

I Fakultät in konstantem Platzaufwand f a c 3 :: I n t e g e r→ I n t e g e r f a c 3 n = f a c ’ n 1 where

f a c ’ n a c c = s e q a c c $ i f n == 0 then a c c e l s e f a c ’ ( n−1) ( n∗a c c )

(23)

Speicherprofil: fac1 50000, nicht optimiert

fac 1 50000 +RTS -hc (null) 228,418 bytes x seconds Tue Jan 15 13:28 2013

bytes

50k 100k 150k 200k 250k 300k

(51)PINNED (99)fac1/main/Main.CAF

(24)

Speicherprofil: fac2 50000, nicht optimiert

fac 2 50000 +RTS -hc (null) 253,330 bytes x seconds Tue Jan 15 13:28 2013

bytes

0k 50k 100k 150k 200k 250k 300k

(51)PINNED (99)fac2/main/Main.CAF

(25)

Speicherprofil: fac3 50000, nicht optimiert

fac 3 50000 +RTS -hc (null) 85,428 bytes x seconds Tue Jan 15 13:29 2013

bytes

20k 40k 60k 80k 100k

(93)GHC.IO.Encoding.CAF (51)PINNED (99)fac3/main/Main.CAF

(26)

Speicherprofil: fac1 50000, optimiert

fac 1 50000 +RTS -hc (null) 179,641 bytes x seconds Tue Jan 15 13:29 2013

bytes

0k 50k 100k 150k 200k 250k

(50)PINNED (98)fac1/main

(27)

Speicherprofil: fac2 50000, optimiert

fac 2 50000 +RTS -hc (null) 90,657 bytes x seconds Tue Jan 15 13:29 2013

bytes

20k 40k 60k 80k 100k

(88)GHC.IO.Encoding.CAF (50)PINNED (98)fac2/main

(28)

Speicherprofil: fac3 50000, optimiert

fac 3 50000 +RTS -hc (null) 85,464 bytes x seconds Tue Jan 15 13:29 2013

bytes

0k 20k 40k 60k 80k 100k

(88)GHC.IO.Encoding.CAF (50)PINNED (98)fac3/main

(29)

Fazit Speicherprofile

I Endrekursionnurbei strikten Funktionenschneller

I Optimierung desghc

I meistausreichendfür Striktheitsanalyse

I abernichtfür Endrekursion

I Deshalb:

I ManuelleÜberführung in Endrekursionsinnvoll

I Compiler-Optimierungfür Striktheit nutzen

(30)

foldr vs. foldl

I foldrist nicht endrekursiv:

f o l d r :: ( a → b → b ) → b → [ a ] → b

f o l d r f z [ ] = z

f o l d r f z ( x : x s ) = f x ( f o l d r f z x s )

I foldlist endrekursiv:

f o l d l :: ( a → b → a ) → a → [ b ] → a

f o l d l f z [ ] = z

f o l d l f z ( x : x s ) = f o l d l f ( f z x ) x s

I foldl’ist striktundendrekursiv:

f o l d l ’ :: ( a→ b→ a )→ a→ [ b ]→ a f o l d l ’ f a [ ] = a

f o l d l ’ f a ( x : x s ) =

l e t a ’ = f a x i n a ’ ‘ s e q ‘ f o l d l ’ f a ’ x s

I Für Monoid(φ,e) gilt:foldrφe l =foldl(flipφ)e l

(31)

Wann welches fold?

I foldlendrekursiv, aber traversiert immer die ganzeListe.

I foldl’fernerstriktundkonstanter Platzaufwand

I Wann welchesfold?

I Strikte Funktionenmitfoldl’falten:

r e v 2 :: [ a ] [ a ]

r e v 2 = f o l d l ’ ( f l i p ( : ) ) [ ]

I Wennnicht die ganze Listebenötigt wird, mit foldrfalten:

a l l :: ( a B o o l ) [ a ] B o o l a l l p = f o l d r ((&&) p ) True

I PotenziellunendlicheListenimmermitfoldrfalten.

(32)

Effizienz durch “unendliche” Datenstrukturen

I Listen müssen nichtendlich repräsentierbarsein:

I Nützlichfür Listen mit unbekannter Länge

I Obacht:Induktion nur fürendlicheListen gültig.

I Beispiel: Fibonacci-Zahlen

I Aus der Kaninchenzucht.

I Sollte jeder Informatiker kennen.

f i b ’ :: I n t I n t e g e r f i b ’ 0 = 1

f i b ’ 1 = 1

f i b ’ n = f i b ’ ( n−1)+ f i b ’ ( n−2)

I Problem:baumartigeRekursion,exponentiellerAufwand.

(33)

Fibonacci-Zahlen als Strom

I Lösung: zuvor berechneteTeilergebnisse wiederverwenden.

I Sei fibs :: [ Integer ] Strom aller Fibonaccizahlen:

f i b s 1 1 2 3 5 8 13 21 34 55

t a i l f i b s 1 2 3 5 8 13 21 34 55

t a i l ( t a i l f i b s ) 2 3 5 8 13 21 34 55

I Damit ergibt sich:

f i b s :: [ I n t e g e r ]

f i b s = 1 : 1 : z i p W i t h (+) f i b s ( t a i l f i b s )

I n-te Fibonaccizahl mit fibs !! n

I Aufwand:linear, da fibs nur einmal ausgewertet wird.

(34)

Implementation und Repräsentation von Datenstrukturen

I Datenstrukturen werden intern durchObjekte in einemHeap repräsentiert

I Bezeichner werden anReferenzenin diesen Heap gebunden

I Unendliche Datenstrukturen haben zyklische Verweise

I Kopf wird nureinmalausgewertet.

c y c l e ( t r a c e " Foo ! " [ 5 ] )

I Anmerkung: unendlich Datenstrukturen nur sinnvoll fürnicht-strikte Funktionen

(35)

Überladene Funktionen sind langsam.

I Typklassen sind elegant aberlangsam.

I Implementierung von Typklassen:Verzeichnis (dictionary) von Klassenfunktionen.

I Überladung wird zurLaufzeitaufgelöst

I Bei kritischen Funktionen:Spezialisierung erzwingendurch Angabe der Signatur

I NB:Zahlen(numerische Literale) sind in Haskell überladen!

I Bsp:facs hat den TypNum a=> a-> a

f a c s n = i f n == 0 then 1 e l s e n∗ f a c s ( n−1)

(36)

Überladene Funktionen sind langsam.

I Typklassen sind elegant aberlangsam.

I Implementierung von Typklassen:Verzeichnis (dictionary) von Klassenfunktionen.

I Überladung wird zurLaufzeitaufgelöst

I Bei kritischen Funktionen:Spezialisierung erzwingendurch Angabe der Signatur

I NB:Zahlen(numerische Literale) sind in Haskell überladen!

I Bsp:facs hat den TypNum a=> a-> a

f a c s n = i f n == 0 then 1 e l s e n∗ f a c s ( n−1)

(37)

Listen als Performance-Falle

I Listen sindkeine Felder oder endliche Abbildungen

I Listen:

I Beliebiglang

I Zugriff aufn-tes Element inlinearerZeit (O(n))

I Abstrakt: frei erzeugter Datentyp aus Kopf und Rest

I FelderArray ix a (ModulArray aus der Standardbücherei )

I FesteGröße (Untermenge vonix)

I Zugriff aufn-tes Element inkonstanterZeit(O(1))

I Abstrakt: Abbildung Index auf Daten

I Endliche AbbildungMap k v (ModulData.Map)

I Beliebige Größe

I Zugriff aufn-tes Element insublinearerZeit (O(logn))

I Abstrakt: Abbildung Schlüsselbereichkauf Wertebereichv

(38)

Zusammenfassung

I Endrekursion:whilefür Haskell.

I Überführung in Endrekursionmeist möglich.

I Noch besser sindstrikte Funktionen.

I Speicherlecksvermeiden: Striktheit undEndrekursion

I Compileroptimierungnutzen

I Datenstrukturen müssen nichtendlich repräsentierbarsein

I Überladene Funktionensind langsam.

I Listensind keine Felder oder endliche Abbildungen.

Referenzen

ÄHNLICHE DOKUMENTE

I Beispiel 3: HTML oder LaTeX oder Word — Typesetting... Programmiersprachen

Praktische Informatik 3: Funktionale Programmierung Vorlesung 5 vom 11.11.2014: Funktionen Höherer Ordnung I..

Praktische Informatik 3: Funktionale Programmierung Vorlesung 5 vom 11.11.2014: Funktionen Höherer Ordnung I.. Christoph Lüth Universität Bremen

I Verzögerte Auswertung effizient, weil nur bei Bedarf ausgewertet wird.. I Aber

Praktische Informatik 3: Funktionale Programmierung Vorlesung 5 vom 13.11.2012: Funktionen Höherer Ordnung I. Christoph Lüth Universität Bremen

I Tutorien: Mo 10-12 MZH 5210 Christian Maeder Mo 16-18 MZH 1380 Rene Wagner Di 8-10 MZH 1100 Diedrich Wolter Di 10-12 MZH 1380 Diedrich Wolter Di 10-12 MZH 1400 Bernd Gersdorf Di

werden gleichzeitig definiert (Rekursion!) I Namen f, y und Parameter (x) überlagern andere I Es gilt die Abseitsregel. I Deshalb: Auf gleiche Einrückung der lokalen

Christoph Lüth &amp; Dennis Walter Universität Bremen Wintersemester