• Keine Ergebnisse gefunden

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

N/A
N/A
Protected

Academic year: 2022

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

Copied!
42
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

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

Christoph Lüth

Universität Bremen

Wintersemester 2014/15

(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 Aktionen und Zustände

I Effizienzaspekte

I Scala — Eine praktische Einführung

I Rückblich & Ausblick

2 [1]

(3)

Organisatorisches

I Fachgespräche: 2/3. Februar oder 9/10. Februar?

I Zusatztermin: 27. Februar

(4)

Inhalt

I Zeitbedarf:Endrekursion — while in 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 [1]

(5)

Inhalt

I Zeitbedarf:Endrekursion — while in 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

(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

5 [1]

(7)

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

(8)

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 Entsprichtgotooder while in imperativen Sprachen.

I Wird inSprungoder Schleifeübersetzt.

I Brauchtkeinen Platzauf dem Stack.

6 [1]

(9)

Einfaches Beispiel

I In Haskell:

even x = i f x<1 then x == 0 else even (x−2)

I Übersetzt nach C:

int even (int x)

{ i f (x<1) return x == 0;

else return (even (x−2)); }

I Äquivalente Formulierung:

int even (int x)

{ i f ( ! ( x< 1)) { x −= 2; return even(x ) ; } else return x == 0; }

I Iterative Variante mit Schleife: int even (int x)

{ while ( ! ( x<1)) x−= 2; return x == 0; }

(10)

Einfaches Beispiel

I In Haskell:

even x = i f x<1 then x == 0 else even (x−2)

I Übersetzt nach C:

int even (int x)

{ i f (x<1) return x == 0;

else return (even (x−2)); }

I Äquivalente Formulierung:

int even (int x)

{ i f ( ! ( x< 1)) { x −= 2; return even(x ) ; } else return x == 0; }

I Iterative Variante mit Schleife: int even (int x)

{ while ( ! ( x<1)) x−= 2; return x == 0; }

7 [1]

(11)

Einfaches Beispiel

I In Haskell:

even x = i f x<1 then x == 0 else even (x−2)

I Übersetzt nach C:

int even (int x)

{ i f (x<1) return x == 0;

else return (even (x−2)); }

I Äquivalente Formulierung:

int even (int x)

{ i f ( ! ( x< 1)) { x −= 2; return even(x ) ; } else return x == 0; }

I Iterative Variante mit Schleife:

int even (int x)

{ while ( ! ( x<1)) x−= 2;

return x == 0; }

(12)

Beispiel: Fakultät

I fac1 nicht endrekursiv:

fac1 :: Integer→ Integer

fac1 n = i f n == 0 then 1 else n ∗ fac1 (n−1)

I fac2 endrekursiv:

fac2 :: Integer→ Integer fac2 n = fac ’ n 1 where

fac ’ :: Integer→ Integer→ Integer fac ’ n acc = i f n == 0 then acc

else fac ’ (n−1) (n∗acc)

I fac1 verbraucht Stack, fac2 nicht.

I Istnichtmerklich schneller?!

8 [1]

(13)

Beispiel: Fakultät

I fac1 nicht endrekursiv:

fac1 :: Integer→ Integer

fac1 n = i f n == 0 then 1 else n ∗ fac1 (n−1)

I fac2 endrekursiv:

fac2 :: Integer→ Integer fac2 n = fac ’ n 1 where

fac ’ :: Integer→ Integer→ Integer fac ’ n acc = i f n == 0 then acc

else fac ’ (n−1) (n∗acc)

I fac1 verbraucht Stack, fac2 nicht.

I Istnichtmerklich schneller?!

(14)

Beispiel: Listen umdrehen

I Liste umdrehen,nicht endrekursiv:

rev ’ :: [ a ]→ [ a ] rev ’ [ ] = [ ]

rev ’ (x : xs ) = rev ’ xs ++ [ x ]

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

I Liste umdrehen,endrekursivund O(n): rev :: [ a ]→ [ a ]

rev xs = rev0 xs [ ] where rev0 [ ] ys = ys

rev0 (x : xs ) ys = rev0 xs (x : ys )

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

I Schneller— warum?

9 [1]

(15)

Beispiel: Listen umdrehen

I Liste umdrehen,nicht endrekursiv:

rev ’ :: [ a ]→ [ a ] rev ’ [ ] = [ ]

rev ’ (x : xs ) = rev ’ xs ++ [ x ]

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

I Liste umdrehen,endrekursivund O(n):

rev :: [ a ]→ [ a ]

rev xs = rev0 xs [ ] where rev0 [ ] ys = ys

rev0 (x : xs ) ys = rev0 xs (x : ys )

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

I Schneller— warum?

(16)

Verzögerte Auswertung und 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: fac2

I Zwischenergebnisse werdennicht auswertet.

I Insbesondere ärgerlich beinicht-terminierenden Funktionen.

10 [1]

(17)

Verzögerte Auswertung und 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: fac2

I Zwischenergebnisse werdennicht auswertet.

I Insbesondere ärgerlich beinicht-terminierenden Funktionen.

(18)

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 ‘ seq ‘ f x

I ghc machtStriktheitsanalyse

I Fakultät in konstantem Platzaufwand fac3 :: Integer→ Integer

fac3 n = fac ’ n 1 where

fac ’ n acc = seq acc $ i f n == 0 then acc else fac ’ (n−1) (n∗acc)

11 [1]

(19)

Speicherprofil: fac1 50000, nicht optimiert

fac 1 50000 +RTS -hc 51,742 bytes x seconds Mon Jan 12 16:17 2015

seconds

0.0 0.0 0.0 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2

bytes

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

(47)PINNED (91)fac1/main/Main.CAF

(20)

Speicherprofil: fac2 50000, nicht optimiert

fac 2 50000 +RTS -hc 56,224 bytes x seconds Mon Jan 12 16:17 2015

seconds

0.0 0.0 0.0 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.2

bytes

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

(47)PINNED (91)fac2/main/Main.CAF

13 [1]

(21)

Speicherprofil: fac3 50000, nicht optimiert

fac 3 50000 +RTS -hc 18,428 bytes x seconds Mon Jan 12 16:17 2015

seconds

0.0 0.0 0.0 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.2

bytes

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

(86)GHC.Conc.Signal.CAF (75)GHC.IO.Handle.FD.CAF (83)GHC.IO.Encoding.CAF (47)PINNED (91)fac3/main/Main.CAF

(22)

Speicherprofil: fac1 50000, optimiert

fac 1 50000 +RTS -hc 76,537 bytes x seconds Mon Jan 12 16:17 2015

seconds

0.0 0.1 0.1 0.2 0.2 0.2

bytes

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

(46)PINNED (90)fac1/main

15 [1]

(23)

Speicherprofil: fac2 50000, optimiert

fac 2 50000 +RTS -hc 25,081 bytes x seconds Mon Jan 12 16:17 2015

seconds

0.0 0.1 0.1 0.2 0.2 0.2

bytes

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

(82)GHC.Conc.Signal.CAF (85)GHC.IO.Handle.FD.CAF (79)GHC.IO.Encoding.CAF (46)PINNED (90)fac2/main

(24)

Speicherprofil: fac3 50000, optimiert

fac 3 50000 +RTS -hc 18,394 bytes x seconds Mon Jan 12 16:17 2015

seconds

0.0 0.0 0.0 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.2

bytes

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

(82)GHC.Conc.Signal.CAF (85)GHC.IO.Handle.FD.CAF (79)GHC.IO.Encoding.CAF (46)PINNED (90)fac3/main

17 [1]

(25)

Fazit Speicherprofile

I Endrekursionnurbei strikten Funktionenschneller

I Optimierung desghc

I Meistausreichend fürStriktheitsanalyse

I Abernichtfür Endrekursion

I Deshalb:

I ManuelleÜberführung in Endrekursionsinnvoll

I Compiler-Optimierungfür Striktheit nutzen

(26)

Ü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)

19 [1]

(27)

Beispiel

I Länge einer Liste (nicht-endrekursiv) length ’ :: [ a ]→ Int

length ’ xs = i f n u l l xs then 0

else 1+ length ’ ( t a i l xs )

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)

(28)

Beispiel

I Damitendrekursive Variante:

length :: [ a ]→ Int

length xs = len xs 0 where

len xs y = i f n u l l xs then y−−was: y+ 0 else len ( t a i l xs ) (1+ y)

I AllgemeinesMuster:

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

I Zusätzlicher Parameterakkumuliert Resultat.

21 [1]

(29)

Weiteres Beispiel: foldr vs. foldl

I foldr istnicht 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 : xs ) = f x ( f o l d r f z xs )

I foldl istendrekursiv:

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 : xs ) = f o l d l f ( f z x) xs

I foldl ’ ist striktund endrekursiv:

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 : xs ) =

let a ’ = f a x in a ’ ‘ seq ‘ f o l d l ’ f a ’ xs

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

(30)

Weiteres Beispiel: foldr vs. foldl

I foldr istnicht 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 : xs ) = f x ( f o l d r f z xs )

I foldl istendrekursiv:

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 : xs ) = f o l d l f ( f z x) xs

I foldl ’ ist striktund endrekursiv:

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 : xs ) =

let a ’ = f a x in a ’ ‘ seq ‘ f o l d l ’ f a ’ xs

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

22 [1]

(31)

Wann welches fold ?

I foldl endrekursiv, aber traversiert immer dieganze Liste.

I foldl ’ fernerstriktundkonstanter Platzaufwand

I Wann welches fold?

I Strikte Funktionenmit foldl ’ falten:

rev2 :: [ a ]→ [ a ]

rev2 = f o l d l ’ ( f l i p ( : ) ) [ ]

I Wennnicht die ganze Listebenötigt wird, mit foldr falten:

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

I PotenziellunendlicheListenimmermit foldr falten.

(32)

Endrekursive Aktionen

I Nicht endrekursiv:

getLines ’ :: IO String getLines ’ = do s t r← getLine

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

else do r e s t← getLines ’ return ( s t r ++ r e s t )

I Endrekursiv:

getLines :: IO String getLines = g e t i t "" where

g e t i t res =do s t r← getLine

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

24 [1]

(33)

Fortgeschrittene Endrekursion

I Akkumulationvon Ergebniswerten durch partiell applizierte Funktionen

I Sonderfall vonContinuations: es wird nicht das Ergebnis zurückgegeben, sondern eine Funktion, welche das Ergebnis erhält

I Beispiel: die KlasseShow

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

class Show a where show :: a→ String

I Deshalb zusätzlich

showsPrec :: Int→ a→ String→ String show x = showsPrec 0 x ""

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

(34)

Fortgeschrittene Endrekursion

I Akkumulationvon Ergebniswerten durch partiell applizierte Funktionen

I Sonderfall vonContinuations: es wird nicht das Ergebnis zurückgegeben, sondern eine Funktion, welche das Ergebnis erhält

I Beispiel: die KlasseShow

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

class Show a where show :: a→ String

I Deshalb zusätzlich

showsPrec :: Int→ a→ String→ String show x = showsPrec 0 x ""

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

25 [1]

(35)

Beispiel: Mengen als Listen

data Set a = Set [ a ] Zu langsamwäre

instance Show a⇒ Show ( Set a) where show ( Set elems) =

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

Deshalb besser

instance Show a⇒ Show ( Set a) where

showsPrec i ( Set elems) = showElems elems where showElems [ ] = ( "{}" ++)

showElems (x : xs ) = ( ’{ ’ : ) ◦shows x◦showl xs where showl [ ] = ( ’} ’ : )

showl (x : xs ) = ( ’ , ’ : ) ◦shows x◦showl xs

(36)

Effizienz durch “unendliche” Datenstrukturen

I Listen müssen nichtendlich repräsentierbarsein:

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

I Allerdings Induktion nur fürendliche Listen gültig.

I Beispiel: Fibonacci-Zahlen

I Aus der Kaninchenzucht.

I Sollte jeder Informatiker kennen.

fib ’ :: Int→ Integer fib ’ 0 = 1

fib ’ 1 = 1

fib ’ n = fib ’ (n−1)+ fib ’ (n−2)

I Problem:baumartigeRekursion,exponentiellerAufwand.

27 [1]

(37)

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 :: [ Integer ]

f i b s = 1 : 1 : zipWith (+) 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.

(38)

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.

cycle ( trace "Foo! " [ 5 ] )

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

29 [1]

(39)

Ü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

facs n = i f n == 0 then 1 else n∗ facs (n−1)

(40)

Ü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

facs n = i f n == 0 then 1 else n∗ facs (n−1)

30 [1]

(41)

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 (Modul Data.Arrayaus 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

I Sonderfall:Set kMap k Bool

(42)

Zusammenfassung

I Endrekursion: while fü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.

32 [1]

Referenzen

ÄHNLICHE DOKUMENTE

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

Praktische Informatik 3: Funktionale Programmierung Vorlesung 13 vom 24.01.17: Scala — Eine praktische Einführung..

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

Praktische Informatik 3: Funktionale Programmierung Vorlesung 13 vom 20.01.15: Scala — Eine praktische Einführung..

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

I Eine Funktion hat ein Speicherleck, wenn Speicher unnötig lange im Zugriff bleibt. I “Echte” Speicherlecks wie in C/C++

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