SlideShare a Scribd company logo
1 of 37
하스켈 성능 튜닝
목 차
1. 하스켈 소개
2. 문제의 발단
3. 성능 개선
4. 결론
하스켈이란 무엇인가
• General-purpose purely functional programming language
• Non-strict semantics (lazy evaluation)
• Strong static type system based on Hindley-Milner type inference
어느 언어나 그럴 듯한 홍보 문구를 가지고 있다
• 순수 함수만 있어서 코드를 이해하기 쉽고
• 각 함수의 정확성을 분석, 검사하기 쉽고
• 고급 타입 시스템이 실수를 줄여준다
타입
pythagoras_cps :: Int -> Int -> ((Int -> r) -> r)
코드
pythagoras_cps x y = k ->
square_cps x $ x_squared ->
square_cps y $ y_squared ->
add_cps x_squared y_squared $ k
분석
pythagoras_cps 3 4 print
= square_cps x $ x2 -> square_cps y $ y2 -> add_cps x2 y2 print
= square_cps x (x2 -> square_cps y (y2 -> add_cps x2 y2 print))
= square_cps x (x2 -> square_cps y (y2 -> print (add x2 y2))
= (x2 -> square_cps y (y2 -> print (add x2 y2)) (square x)
= ((x2 -> (y2 -> print (add x2 y2)) (square y)) (square x)
= (y2 -> print (add (square x) y2)) (square y)
= print (add (square x) (square y))
우 와! 정말 쉽다~ 대단해!
수학 기호와 닮았다
𝑓: 𝑎, 𝑏 → 𝑅3
𝑓 𝑎, 𝑏 = (𝑎 𝑐𝑜𝑠𝑏, 𝑎 𝑠𝑖𝑛𝑏, 𝑎)
𝑔 𝑥 =
𝑠𝑖𝑛𝑥
𝑥
, 𝑥 < 0
1, 𝑥 = 0
𝑒 𝑥
+ 1, 𝑥 > 0
type Curve = (Double, Doule, Double)
f :: (Double, Double) -> Curve
f a b = (a * cos b, a * sin b, a)
g x
| x < 0 = sin x / x
| x == 0 = 1
| x > 0 = exp x + 1
수학 기호와 닮았다
𝑆 = 𝑓(𝑥) 1 ≤ 𝑥 ≤ 10, 𝑥 𝑖𝑠 𝑖𝑛𝑡𝑒𝑔𝑒𝑟 }
where 𝑓 𝑥 = 𝑥3
− 𝑥
𝑥 𝑟, 𝜃 = 𝑟 𝑐𝑜𝑠𝜃, 𝑟 𝑠𝑖𝑛𝜃, 𝑟
Let 𝑟 𝑡 = 𝑒 𝑡/2, 𝜃 𝑡 =
𝑡
2
𝛼 𝑡 = 𝑥 𝑟 𝑡 , 𝜃 𝑡
s = [ f x | x <- [1..10] ]
where f x = x*x*x - x
x r theta = (r * cos theta, r * sin theta, r)
a t = let r = exp (t/2)
theta = t / (sqrt 2)
in x r theta
수학 = = 하스켈
A real vector space is a set V, whose elements are called vectors, together with two binary operations + ∶
𝑉 × 𝑉 → 𝑉 and • ∶ 𝑅 × 𝑉 → 𝑉 , called addition and scalar multiplication, which satisfy the following eight
axioms for all 𝒖, 𝒗, 𝒘 ∈ 𝑉 and 𝑟, 𝑠 ∈ 𝑹
• 𝒖 + 𝒗 = 𝒗 + 𝒖
• 𝒖 + 𝒗 + 𝒘 = 𝒖 + 𝒗 + 𝒘
• there is an element 𝟎 in 𝑉 s.t. 𝟎 + 𝒖 = 𝒖
• 𝑟𝑠 ⋅ 𝒖 = 𝑟 ⋅ 𝑠 ⋅ 𝒖
• 𝑟 + 𝑠 ⋅ 𝒖 = 𝑟 ⋅ 𝒖 + 𝑠 ⋅ 𝒖
• r ⋅ 𝒖 + 𝒗 = 𝑟 ⋅ 𝒖 + 𝑟 ⋅ 𝒗
• 0 ⋅ 𝒖 = 𝟎
• 1 ⋅ 𝒖 = 𝟏
수학 = = 하스켈
class Monoid a where
mempty :: a
mappend :: a -> a -> a
mconcat :: [a] -> a
mconcat = foldr mappend mempty
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
sequenceA :: Applicative f => t (f a) -> f (t a)
mapM :: Monad m => (a -> m b) -> t a -> m (t b)
sequence :: Monad m => t (m a) -> m (t a)
chainCPS :: ((a -> r) -> r) -> (a -> ((b -> r) -> r)) -> ((b -> r) -> r)
chainCPS s f = k -> s $ x -> f x $ k
foldMap (g . f) = g . foldMap f
foldMap f = fold . fmap f
foldMap g . fmap f = foldMap (g . f) = g . foldMap f
instance Arrow Circuit where
arr f = Circuit $ a -> (arr f, f a)
first (Circuit cir) = Circuit $ (b, d) ->
let (cir', c) = cir b
in (first cir', (c, d))
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
f >=> g = x -> f x >>= g
하스켈에서 가능한 것들
• 소수 구하기
primes = sieve [2..] where sieve (p:xs) = p : sieve [ n | n <- xs, n `mod` p > 0 ]
• 문자열을 공백으로 분리 “Hello world” => [“Hello”, “world”]
unfoldr (b -> fmap (const . (second $ drop 1) . break (==' ') $ b) . listToMaybe $ b)0 ]
• N-queen 알고리즘
queens n = foldM f [] [1..n] where
f qs _ = [q:qs | q <- [1..n]  qs, q `notDiag` qs]
q `notDiag` qs = and [abs (q - qi) /= i | (qi,i) <- qs `zip` [1..]]
• 입력을 알파벳 순으로 정렬
main = (mapM_ putStrLn . Data.List.sort . lines) =<< getContents
사건의 전말
온라인 저지 사이트 ALGOSPOT
문제 ID: WEIRD (https://www.algospot.com/judge/problem/read/WEIRD)
In mathematics, weird numbers are natural numbers that are abundant but not semiperfect. In other words, a
natural number N is a weird number if and only if:
• Sum of its proper divisors (i.e. less than N ) is greater than the number.
• No subset of its divisors sum to N.
For example, the set of proper divisors of 12 is { 1, 2, 3, 4, 6 } . The sum of these numbers exceed 12, however,
12 is not a weird number since 1 + 2 + 3 + 6 = 12.
However, 70 is a weird number since its proper divisors are {1, 2, 5, 7, 10, 14, 35} and no subset sums to 70 .
Write a program to determine if the given numbers are weird or not.
문제 분석
S(N) = N의 약수들의 집합 (N은 제외)
• S의 원소들의 합이 N보다 크다 (abundant)
• S의 부분집합 중 그 합이 N이 되는 집합이 있다 (semiperfect)
• N이 abundant하면서 semiperfect하지 않으면 weired number
• 예시
• S(12) = {1, 2, 3, 4, 6}
• sum(S) = 16 > 12 -> 12는 abundant
• sum({1,2,3,6}) = 12 = N -> 12는 semiperfect
• 따라서 12는 weired number가 아니다
간단한 0-1 KNAPSACK 문제
• S(N)을 알면 abundant 검사는 단순하다
• semiperfect 검사는 0-1 knapsack 문제
• 약수들은 정수이므로 다이나믹 프로그래밍으로 해결 가능
• 𝑆 𝑁 = 𝑠1, 𝑠2, … , 𝑠 𝑘 (𝑠1 ≤ 𝑠2 ≤ … )
• 𝑇 𝑖, 𝑗 = 𝑆 𝑁 의 𝑠1, 𝑠2, … , 𝑠𝑖 까지 써서 만들 수 있는, j 이하의 최대 합
• 𝑇 𝑖, 𝑗 =
0 𝑖 = 0
𝑇 𝑖 − 1, 𝑗 𝑠𝑖 > 𝑗
max 𝑇 𝑖 − 1, 𝑗 , 𝑇 𝑖 − 1, 𝑗 − 𝑠𝑖 + 𝑠𝑖 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒
• 𝑇 𝑘, 𝑁 = 𝑁 이면 N은 semiperfect
간단한 0-1 KNAPSACK 문제
N = 12 1 2 3 4 5 6 7 8 9 10 11 12
s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0
s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1
s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3
s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6
s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10
s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
• abundant하지만 semiperfect이다
• N = 12는 weird number가 아니다
익숙한 언어들의 해법
N = 12 1 2 3 4 5 6 7 8 9 10 11 12
s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0
s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1
s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3
s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6
s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10
s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
• 계산 테이블을 준비한다
• 인덱스가 낮은 원소부터 차례대로 계산한다
익숙한 언어들의 해법
N = 12 1 2 3 4 5 6 7 8 9 10 11 12
s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0
s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1
s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3
s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6
s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10
s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
• 계산 테이블을 준비한다
• 인덱스가 낮은 원소부터 차례대로 계산한다
익숙한 언어들의 해법
• 계산 테이블을 준비한다
• 인덱스가 낮은 원소부터 차례대로 계산한다
N = 12 1 2 3 4 5 6 7 8 9 10 11 12
s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0
s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1
s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3
s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6
s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10
s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
익숙한 언어들의 해법
• 계산 테이블을 준비한다
• 인덱스가 낮은 원소부터 차례대로 계산한다
N = 12 1 2 3 4 5 6 7 8 9 10 11 12
s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0
s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1
s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3
s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6
s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10
s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
익숙한 언어들의 해법
• 계산 테이블을 준비한다
• 인덱스가 낮은 원소부터 차례대로 계산한다
N = 12 1 2 3 4 5 6 7 8 9 10 11 12
s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0
s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1
s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3
s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6
s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10
s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
이렇게 계산하는 이유?
• 점화식을 그대로 계산하면 같은 원소를 중복 계산
• 계산 시간이 지수함수적으로 증가 (가장 단순한 예: 피보나치 수의 재귀적 계산)
• 큰 문제가 작은 문제를 포함
• 작은 문제를 풀어두면 큰 문제를 풀 수 있다
• 테이블을 계산해두면 비슷한 문제들을 풀 때 재활용 가능
• 메모리를 희생하고 속도를 얻는다
하스켈식 해법
1 divisors n = [ x | x <- [1..(n-1)], n `mod` x == 0]
2
3 knapsack n divs = table where
4 table :: Int -> Int -> Int
5 table 0 j = 0
6 table i j
7 | si > j = table (i-1) j
8 | otherwise = maximum [ table (i-1) j, table (i-1) (j-si) + si ]
9 where si = divs !! i
10
11 isWeird n = abundant && not semiperfect where
12 divs = divisors n
13 abundant = sum divs > n
14 semiperfect = (knapsack n divs) (length divs - 1) n == n
15
16 main = do
17 putStrLn $ "is 12 weird?: " ++ show (isWeird 12)
18 putStrLn $ "12 70 weird?: " ++ show (isWeird 70)
하스켈식 해법
1 divisors n = [ x | x <- [1..(n-1)], n `mod` x == 0]
2
3 knapsack n divs = table where
4 table :: Int -> Int -> Int
5 table 0 j = 0
6 table i j
7 | si > j = table (i-1) j
8 | otherwise = maximum [ table (i-1) j, table (i-1) (j-si) + si ]
9 where si = divs !! i
10
11 isWeird n = abundant && not semiperfect where
12 divs = divisors n
13 abundant = sum divs > n
14 semiperfect = (knapsack n divs) (length divs - 1) n == n
15
16 main = do
17 putStrLn $ "is 12 weird?: " ++ show (isWeird 12)
18 putStrLn $ "12 70 weird?: " ++ show (isWeird 70)
점화식 중복 계산
진짜 하스켈식 해법
• 테이블 자체를 재귀적으로 정의하면 안된다
• 테이블의 원소를 재귀적으로 정의하면 된다
• ???
knapsack n divs len = table where
table = array ((0,0),(len,n))
$! [((0,w),0) | w <- [0..n]]
++ [((i,w),v) | i<-[1..len], w<-[0..n],
let v = maximum [at(i-1,w), divs!i + at(i-1,w-(divs!i))]]
at (i, w) = if w < 0 then -n else table ! (i,w)
메모이제이션(MEMOIZATION)
• 테이블 계산하는 게 메모이제이션
• 하스켈에서는 테이블의 원소를 재귀적으로 정의해도 메모이제이션이 가능하다
• 지연 평가(Lazy Evaluation)
• 데이터 불변성(Data Immutability)
• 고정점 연산자(Fixed Point Operator)
성능을 측정해보자
• 문제에서 주어진 조건은 1 <= N <= 500000
• N = 500000에 대해 테스트
무엇이 문제인가
• 지연 평가
• 데이터타입
• 알고리즘 자체의 문제
무엇이 문제인가 – 지연 평가
• 값이 실제로 필요하기 전에는 계산하지 않는다
• 1에서 N까지 더하기
• foldr (+) 0 [1..N]
• 메모리 복잡도는 O(1)?
• foldr (+) 0 [1..3] = 1 + (foldr (+) 0 [2..3]) = 1 + (2 + foldr (+) 0 [3]) = 1 + (2 + (3 + (foldr (+) 0 [])))
= 1 + (2 + (3 + 0)) = 1 + (2 + 3) = 1 + 5 = 6
• 메모리 복잡도가 O(N)
• 핵심: 표현식이 한없이 길어진다
이 계산 트리가 완성된 후 계산 시작
무엇이 문제인가 – 데이터타입
• C언어
• int n;
• 4바이트
• 하스켈
• n :: Int
• data Int = I# Int#
• 2워드 = 8바이트
• 박싱(boxing), 언박싱(unboxing) 필요
대응
무엇이 문제인가 – 애초에 알고리즘이 구려
• 테이블로 돌아가보자
• I번 행을 계산하는 데 (i-1)번 행만 필요 – 전체 테이블을 유지할 필요가 없다
N = 12 1 2 3 4 5 6 7 8 9 10 11 12
s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0
s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1
s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3
s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6
s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10
s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
해결 방법
• 철저한 평가(Strict Evaluation)
• 표현식이 너무 길어지기 전에 계산을 강제로 수행
• 원시 타입(Unboxed type)
• 정수 표현에 C와 같은 바이트 사용
• 테이블의 두 행만 메모리에 유지
• 필요없는 행은 바로 가비지 컬렉션이 되도록
해결 방법
knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int
knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where
a prevRow prevA
| prevRow == len = prevA
| True = currA `seq` a (prevRow+1) currA where
currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n],
let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]]
at w = if w < 0 then -n else prevA ! w
해결 방법
knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int
knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where
a prevRow prevA
| prevRow == len = prevA
| True = currA `seq` a (prevRow+1) currA where
currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n],
let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]]
at w = if w < 0 then -n else prevA ! w
철저한 평가
해결 방법
knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int
knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where
a prevRow prevA
| prevRow == len = prevA
| True = currA `seq` a (prevRow+1) currA where
currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n],
let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]]
at w = if w < 0 then -n else prevA ! w
Unboxed 타입
해결 방법
knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int
knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where
a prevRow prevA
| prevRow == len = prevA
| True = currA `seq` a (prevRow+1) currA where
currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n],
let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]]
at w = if w < 0 then -n else prevA ! w
일부 행만 유지
성능을 측정해보자
• 다시 N = 500000에 대해 테스트
• 메모리 사용 1/1000
• 속도 2.5배
결론
온라인 저지에 하스켈을 쓰지 말자
추가 자료
• 고성능 하스켈
• http://www.slideshare.net/tibbe/highperformance-haskell
• 하스켈 퍼포먼스
• https://www.youtube.com/watch?v=_pDUq0nNjhI
• 하스켈 동시성 & 병렬 프로그래밍
• http://chimera.labs.oreilly.com/books/1230000000929/index.html
감사합니다
Q&A

More Related Content

What's hot

알고리즘과 자료구조
알고리즘과 자료구조알고리즘과 자료구조
알고리즘과 자료구조영기 김
 
Sicp 2.2 계층 구조 데이터와 닫힘 성질
Sicp 2.2 계층 구조 데이터와 닫힘 성질Sicp 2.2 계층 구조 데이터와 닫힘 성질
Sicp 2.2 계층 구조 데이터와 닫힘 성질aceigy6322
 
함수형 프로그래밍? 그래서 어떻게
함수형 프로그래밍? 그래서 어떻게함수형 프로그래밍? 그래서 어떻게
함수형 프로그래밍? 그래서 어떻게Gyooha Kim
 
종이접기(fold) 프로그래밍
종이접기(fold) 프로그래밍종이접기(fold) 프로그래밍
종이접기(fold) 프로그래밍Kwang Yul Seo
 
알고리즘 스터디(정렬) Seungdols
알고리즘 스터디(정렬) Seungdols알고리즘 스터디(정렬) Seungdols
알고리즘 스터디(정렬) Seungdolsseungdols
 
Coursera Machine Learning으로 기계학습 배우기 : week2
Coursera Machine Learning으로 기계학습 배우기 : week2Coursera Machine Learning으로 기계학습 배우기 : week2
Coursera Machine Learning으로 기계학습 배우기 : week2Kwangsik Lee
 
2021 2학기 정기 세미나 4주차
2021 2학기 정기 세미나 4주차2021 2학기 정기 세미나 4주차
2021 2학기 정기 세미나 4주차Moonki Choi
 
Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀beom kyun choi
 
R 스터디 네번째
R 스터디 네번째R 스터디 네번째
R 스터디 네번째Jaeseok Park
 
하스켈 모나드
하스켈 모나드하스켈 모나드
하스켈 모나드Kwang Yul Seo
 
확통 회귀분석
확통 회귀분석확통 회귀분석
확통 회귀분석jaypi Ko
 
쏙 알고스터디 01
쏙 알고스터디 01쏙 알고스터디 01
쏙 알고스터디 01Jisu Lee
 
해커에게 전해들은 머신러닝 #3
해커에게 전해들은 머신러닝 #3해커에게 전해들은 머신러닝 #3
해커에게 전해들은 머신러닝 #3Haesun Park
 
차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)beom kyun choi
 
세미나
세미나세미나
세미나Dongyi Kim
 
2021 2학기 정기 세미나 5주차
2021 2학기 정기 세미나 5주차2021 2학기 정기 세미나 5주차
2021 2학기 정기 세미나 5주차Moonki Choi
 
R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1happychallenge
 
[신경망기초] 신경망의시작-퍼셉트론
[신경망기초] 신경망의시작-퍼셉트론[신경망기초] 신경망의시작-퍼셉트론
[신경망기초] 신경망의시작-퍼셉트론jaypi Ko
 
[신경망기초] 선형회귀분석
[신경망기초] 선형회귀분석[신경망기초] 선형회귀분석
[신경망기초] 선형회귀분석jaypi Ko
 

What's hot (20)

알고리즘과 자료구조
알고리즘과 자료구조알고리즘과 자료구조
알고리즘과 자료구조
 
Sicp 2.2 계층 구조 데이터와 닫힘 성질
Sicp 2.2 계층 구조 데이터와 닫힘 성질Sicp 2.2 계층 구조 데이터와 닫힘 성질
Sicp 2.2 계층 구조 데이터와 닫힘 성질
 
함수형 프로그래밍? 그래서 어떻게
함수형 프로그래밍? 그래서 어떻게함수형 프로그래밍? 그래서 어떻게
함수형 프로그래밍? 그래서 어떻게
 
종이접기(fold) 프로그래밍
종이접기(fold) 프로그래밍종이접기(fold) 프로그래밍
종이접기(fold) 프로그래밍
 
알고리즘 스터디(정렬) Seungdols
알고리즘 스터디(정렬) Seungdols알고리즘 스터디(정렬) Seungdols
알고리즘 스터디(정렬) Seungdols
 
Coursera Machine Learning으로 기계학습 배우기 : week2
Coursera Machine Learning으로 기계학습 배우기 : week2Coursera Machine Learning으로 기계학습 배우기 : week2
Coursera Machine Learning으로 기계학습 배우기 : week2
 
2021 2학기 정기 세미나 4주차
2021 2학기 정기 세미나 4주차2021 2학기 정기 세미나 4주차
2021 2학기 정기 세미나 4주차
 
Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀
 
R 스터디 네번째
R 스터디 네번째R 스터디 네번째
R 스터디 네번째
 
하스켈 모나드
하스켈 모나드하스켈 모나드
하스켈 모나드
 
확통 회귀분석
확통 회귀분석확통 회귀분석
확통 회귀분석
 
쏙 알고스터디 01
쏙 알고스터디 01쏙 알고스터디 01
쏙 알고스터디 01
 
해커에게 전해들은 머신러닝 #3
해커에게 전해들은 머신러닝 #3해커에게 전해들은 머신러닝 #3
해커에게 전해들은 머신러닝 #3
 
차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)
 
세미나
세미나세미나
세미나
 
2021 2학기 정기 세미나 5주차
2021 2학기 정기 세미나 5주차2021 2학기 정기 세미나 5주차
2021 2학기 정기 세미나 5주차
 
R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1
 
Linear algebra
Linear algebraLinear algebra
Linear algebra
 
[신경망기초] 신경망의시작-퍼셉트론
[신경망기초] 신경망의시작-퍼셉트론[신경망기초] 신경망의시작-퍼셉트론
[신경망기초] 신경망의시작-퍼셉트론
 
[신경망기초] 선형회귀분석
[신경망기초] 선형회귀분석[신경망기초] 선형회귀분석
[신경망기초] 선형회귀분석
 

Similar to 하스켈 성능 튜닝

Code로 이해하는 RNN
Code로 이해하는 RNNCode로 이해하는 RNN
Code로 이해하는 RNNSANG WON PARK
 
3.neural networks
3.neural networks3.neural networks
3.neural networksHaesun Park
 
세그먼트 트리 느리게 업데이트하기 - Sogang ICPC Team, 2020 Winter
세그먼트 트리 느리게 업데이트하기 - Sogang ICPC Team, 2020 Winter세그먼트 트리 느리게 업데이트하기 - Sogang ICPC Team, 2020 Winter
세그먼트 트리 느리게 업데이트하기 - Sogang ICPC Team, 2020 WinterSuhyun Park
 
고등학생 R&E Python summary for test
고등학생 R&E Python summary for test고등학생 R&E Python summary for test
고등학생 R&E Python summary for testKyunghoon Kim
 
2.linear regression and logistic regression
2.linear regression and logistic regression2.linear regression and logistic regression
2.linear regression and logistic regressionHaesun Park
 
퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 Winter
퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 Winter퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 Winter
퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 WinterSuhyun Park
 
Deep Learning from scratch 5장 : backpropagation
 Deep Learning from scratch 5장 : backpropagation Deep Learning from scratch 5장 : backpropagation
Deep Learning from scratch 5장 : backpropagationJinSooKim80
 
선형대수 08. 선형 변환 (Linear Transformation)
선형대수 08. 선형 변환 (Linear Transformation)선형대수 08. 선형 변환 (Linear Transformation)
선형대수 08. 선형 변환 (Linear Transformation)AHRA CHO
 
페리 수열(Farey sequence)
페리 수열(Farey sequence)페리 수열(Farey sequence)
페리 수열(Farey sequence)종빈 오
 
2012 Dm C3 03
2012 Dm C3 032012 Dm C3 03
2012 Dm C3 03chl132435
 
RLCode와 A3C 쉽고 깊게 이해하기
RLCode와 A3C 쉽고 깊게 이해하기RLCode와 A3C 쉽고 깊게 이해하기
RLCode와 A3C 쉽고 깊게 이해하기Woong won Lee
 
03. linear regression
03. linear regression03. linear regression
03. linear regressionJeonghun Yoon
 
[데브루키]노대영_알고리즘 스터디
[데브루키]노대영_알고리즘 스터디[데브루키]노대영_알고리즘 스터디
[데브루키]노대영_알고리즘 스터디대영 노
 
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019Suhyun Park
 
이산치3보고서
이산치3보고서이산치3보고서
이산치3보고서KimChangHoen
 
[D2CAMPUS] Algorithm tips - ALGOS
[D2CAMPUS] Algorithm tips - ALGOS[D2CAMPUS] Algorithm tips - ALGOS
[D2CAMPUS] Algorithm tips - ALGOSNAVER D2
 

Similar to 하스켈 성능 튜닝 (20)

Code로 이해하는 RNN
Code로 이해하는 RNNCode로 이해하는 RNN
Code로 이해하는 RNN
 
3.neural networks
3.neural networks3.neural networks
3.neural networks
 
세그먼트 트리 느리게 업데이트하기 - Sogang ICPC Team, 2020 Winter
세그먼트 트리 느리게 업데이트하기 - Sogang ICPC Team, 2020 Winter세그먼트 트리 느리게 업데이트하기 - Sogang ICPC Team, 2020 Winter
세그먼트 트리 느리게 업데이트하기 - Sogang ICPC Team, 2020 Winter
 
Rdatamining
Rdatamining Rdatamining
Rdatamining
 
고등학생 R&E Python summary for test
고등학생 R&E Python summary for test고등학생 R&E Python summary for test
고등학생 R&E Python summary for test
 
2.linear regression and logistic regression
2.linear regression and logistic regression2.linear regression and logistic regression
2.linear regression and logistic regression
 
퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 Winter
퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 Winter퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 Winter
퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 Winter
 
R_datamining
R_dataminingR_datamining
R_datamining
 
Deep Learning from scratch 5장 : backpropagation
 Deep Learning from scratch 5장 : backpropagation Deep Learning from scratch 5장 : backpropagation
Deep Learning from scratch 5장 : backpropagation
 
선형대수 08. 선형 변환 (Linear Transformation)
선형대수 08. 선형 변환 (Linear Transformation)선형대수 08. 선형 변환 (Linear Transformation)
선형대수 08. 선형 변환 (Linear Transformation)
 
Variational AutoEncoder(VAE)
Variational AutoEncoder(VAE)Variational AutoEncoder(VAE)
Variational AutoEncoder(VAE)
 
페리 수열(Farey sequence)
페리 수열(Farey sequence)페리 수열(Farey sequence)
페리 수열(Farey sequence)
 
2012 Dm C3 03
2012 Dm C3 032012 Dm C3 03
2012 Dm C3 03
 
RLCode와 A3C 쉽고 깊게 이해하기
RLCode와 A3C 쉽고 깊게 이해하기RLCode와 A3C 쉽고 깊게 이해하기
RLCode와 A3C 쉽고 깊게 이해하기
 
03. linear regression
03. linear regression03. linear regression
03. linear regression
 
[데브루키]노대영_알고리즘 스터디
[데브루키]노대영_알고리즘 스터디[데브루키]노대영_알고리즘 스터디
[데브루키]노대영_알고리즘 스터디
 
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
 
이산치3보고서
이산치3보고서이산치3보고서
이산치3보고서
 
[D2CAMPUS] Algorithm tips - ALGOS
[D2CAMPUS] Algorithm tips - ALGOS[D2CAMPUS] Algorithm tips - ALGOS
[D2CAMPUS] Algorithm tips - ALGOS
 
알고리즘2
알고리즘2알고리즘2
알고리즘2
 

하스켈 성능 튜닝

  • 2. 목 차 1. 하스켈 소개 2. 문제의 발단 3. 성능 개선 4. 결론
  • 3. 하스켈이란 무엇인가 • General-purpose purely functional programming language • Non-strict semantics (lazy evaluation) • Strong static type system based on Hindley-Milner type inference
  • 4. 어느 언어나 그럴 듯한 홍보 문구를 가지고 있다 • 순수 함수만 있어서 코드를 이해하기 쉽고 • 각 함수의 정확성을 분석, 검사하기 쉽고 • 고급 타입 시스템이 실수를 줄여준다 타입 pythagoras_cps :: Int -> Int -> ((Int -> r) -> r) 코드 pythagoras_cps x y = k -> square_cps x $ x_squared -> square_cps y $ y_squared -> add_cps x_squared y_squared $ k 분석 pythagoras_cps 3 4 print = square_cps x $ x2 -> square_cps y $ y2 -> add_cps x2 y2 print = square_cps x (x2 -> square_cps y (y2 -> add_cps x2 y2 print)) = square_cps x (x2 -> square_cps y (y2 -> print (add x2 y2)) = (x2 -> square_cps y (y2 -> print (add x2 y2)) (square x) = ((x2 -> (y2 -> print (add x2 y2)) (square y)) (square x) = (y2 -> print (add (square x) y2)) (square y) = print (add (square x) (square y)) 우 와! 정말 쉽다~ 대단해!
  • 5. 수학 기호와 닮았다 𝑓: 𝑎, 𝑏 → 𝑅3 𝑓 𝑎, 𝑏 = (𝑎 𝑐𝑜𝑠𝑏, 𝑎 𝑠𝑖𝑛𝑏, 𝑎) 𝑔 𝑥 = 𝑠𝑖𝑛𝑥 𝑥 , 𝑥 < 0 1, 𝑥 = 0 𝑒 𝑥 + 1, 𝑥 > 0 type Curve = (Double, Doule, Double) f :: (Double, Double) -> Curve f a b = (a * cos b, a * sin b, a) g x | x < 0 = sin x / x | x == 0 = 1 | x > 0 = exp x + 1
  • 6. 수학 기호와 닮았다 𝑆 = 𝑓(𝑥) 1 ≤ 𝑥 ≤ 10, 𝑥 𝑖𝑠 𝑖𝑛𝑡𝑒𝑔𝑒𝑟 } where 𝑓 𝑥 = 𝑥3 − 𝑥 𝑥 𝑟, 𝜃 = 𝑟 𝑐𝑜𝑠𝜃, 𝑟 𝑠𝑖𝑛𝜃, 𝑟 Let 𝑟 𝑡 = 𝑒 𝑡/2, 𝜃 𝑡 = 𝑡 2 𝛼 𝑡 = 𝑥 𝑟 𝑡 , 𝜃 𝑡 s = [ f x | x <- [1..10] ] where f x = x*x*x - x x r theta = (r * cos theta, r * sin theta, r) a t = let r = exp (t/2) theta = t / (sqrt 2) in x r theta
  • 7. 수학 = = 하스켈 A real vector space is a set V, whose elements are called vectors, together with two binary operations + ∶ 𝑉 × 𝑉 → 𝑉 and • ∶ 𝑅 × 𝑉 → 𝑉 , called addition and scalar multiplication, which satisfy the following eight axioms for all 𝒖, 𝒗, 𝒘 ∈ 𝑉 and 𝑟, 𝑠 ∈ 𝑹 • 𝒖 + 𝒗 = 𝒗 + 𝒖 • 𝒖 + 𝒗 + 𝒘 = 𝒖 + 𝒗 + 𝒘 • there is an element 𝟎 in 𝑉 s.t. 𝟎 + 𝒖 = 𝒖 • 𝑟𝑠 ⋅ 𝒖 = 𝑟 ⋅ 𝑠 ⋅ 𝒖 • 𝑟 + 𝑠 ⋅ 𝒖 = 𝑟 ⋅ 𝒖 + 𝑠 ⋅ 𝒖 • r ⋅ 𝒖 + 𝒗 = 𝑟 ⋅ 𝒖 + 𝑟 ⋅ 𝒗 • 0 ⋅ 𝒖 = 𝟎 • 1 ⋅ 𝒖 = 𝟏
  • 8. 수학 = = 하스켈 class Monoid a where mempty :: a mappend :: a -> a -> a mconcat :: [a] -> a mconcat = foldr mappend mempty class (Functor t, Foldable t) => Traversable t where traverse :: Applicative f => (a -> f b) -> t a -> f (t b) sequenceA :: Applicative f => t (f a) -> f (t a) mapM :: Monad m => (a -> m b) -> t a -> m (t b) sequence :: Monad m => t (m a) -> m (t a) chainCPS :: ((a -> r) -> r) -> (a -> ((b -> r) -> r)) -> ((b -> r) -> r) chainCPS s f = k -> s $ x -> f x $ k foldMap (g . f) = g . foldMap f foldMap f = fold . fmap f foldMap g . fmap f = foldMap (g . f) = g . foldMap f instance Arrow Circuit where arr f = Circuit $ a -> (arr f, f a) first (Circuit cir) = Circuit $ (b, d) -> let (cir', c) = cir b in (first cir', (c, d)) (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c f >=> g = x -> f x >>= g
  • 9. 하스켈에서 가능한 것들 • 소수 구하기 primes = sieve [2..] where sieve (p:xs) = p : sieve [ n | n <- xs, n `mod` p > 0 ] • 문자열을 공백으로 분리 “Hello world” => [“Hello”, “world”] unfoldr (b -> fmap (const . (second $ drop 1) . break (==' ') $ b) . listToMaybe $ b)0 ] • N-queen 알고리즘 queens n = foldM f [] [1..n] where f qs _ = [q:qs | q <- [1..n] qs, q `notDiag` qs] q `notDiag` qs = and [abs (q - qi) /= i | (qi,i) <- qs `zip` [1..]] • 입력을 알파벳 순으로 정렬 main = (mapM_ putStrLn . Data.List.sort . lines) =<< getContents
  • 10. 사건의 전말 온라인 저지 사이트 ALGOSPOT 문제 ID: WEIRD (https://www.algospot.com/judge/problem/read/WEIRD) In mathematics, weird numbers are natural numbers that are abundant but not semiperfect. In other words, a natural number N is a weird number if and only if: • Sum of its proper divisors (i.e. less than N ) is greater than the number. • No subset of its divisors sum to N. For example, the set of proper divisors of 12 is { 1, 2, 3, 4, 6 } . The sum of these numbers exceed 12, however, 12 is not a weird number since 1 + 2 + 3 + 6 = 12. However, 70 is a weird number since its proper divisors are {1, 2, 5, 7, 10, 14, 35} and no subset sums to 70 . Write a program to determine if the given numbers are weird or not.
  • 11. 문제 분석 S(N) = N의 약수들의 집합 (N은 제외) • S의 원소들의 합이 N보다 크다 (abundant) • S의 부분집합 중 그 합이 N이 되는 집합이 있다 (semiperfect) • N이 abundant하면서 semiperfect하지 않으면 weired number • 예시 • S(12) = {1, 2, 3, 4, 6} • sum(S) = 16 > 12 -> 12는 abundant • sum({1,2,3,6}) = 12 = N -> 12는 semiperfect • 따라서 12는 weired number가 아니다
  • 12. 간단한 0-1 KNAPSACK 문제 • S(N)을 알면 abundant 검사는 단순하다 • semiperfect 검사는 0-1 knapsack 문제 • 약수들은 정수이므로 다이나믹 프로그래밍으로 해결 가능 • 𝑆 𝑁 = 𝑠1, 𝑠2, … , 𝑠 𝑘 (𝑠1 ≤ 𝑠2 ≤ … ) • 𝑇 𝑖, 𝑗 = 𝑆 𝑁 의 𝑠1, 𝑠2, … , 𝑠𝑖 까지 써서 만들 수 있는, j 이하의 최대 합 • 𝑇 𝑖, 𝑗 = 0 𝑖 = 0 𝑇 𝑖 − 1, 𝑗 𝑠𝑖 > 𝑗 max 𝑇 𝑖 − 1, 𝑗 , 𝑇 𝑖 − 1, 𝑗 − 𝑠𝑖 + 𝑠𝑖 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒 • 𝑇 𝑘, 𝑁 = 𝑁 이면 N은 semiperfect
  • 13. 간단한 0-1 KNAPSACK 문제 N = 12 1 2 3 4 5 6 7 8 9 10 11 12 s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1 s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3 s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6 s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10 s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12 • abundant하지만 semiperfect이다 • N = 12는 weird number가 아니다
  • 14. 익숙한 언어들의 해법 N = 12 1 2 3 4 5 6 7 8 9 10 11 12 s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1 s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3 s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6 s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10 s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12 • 계산 테이블을 준비한다 • 인덱스가 낮은 원소부터 차례대로 계산한다
  • 15. 익숙한 언어들의 해법 N = 12 1 2 3 4 5 6 7 8 9 10 11 12 s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1 s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3 s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6 s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10 s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12 • 계산 테이블을 준비한다 • 인덱스가 낮은 원소부터 차례대로 계산한다
  • 16. 익숙한 언어들의 해법 • 계산 테이블을 준비한다 • 인덱스가 낮은 원소부터 차례대로 계산한다 N = 12 1 2 3 4 5 6 7 8 9 10 11 12 s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1 s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3 s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6 s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10 s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
  • 17. 익숙한 언어들의 해법 • 계산 테이블을 준비한다 • 인덱스가 낮은 원소부터 차례대로 계산한다 N = 12 1 2 3 4 5 6 7 8 9 10 11 12 s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1 s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3 s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6 s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10 s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
  • 18. 익숙한 언어들의 해법 • 계산 테이블을 준비한다 • 인덱스가 낮은 원소부터 차례대로 계산한다 N = 12 1 2 3 4 5 6 7 8 9 10 11 12 s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1 s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3 s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6 s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10 s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
  • 19. 이렇게 계산하는 이유? • 점화식을 그대로 계산하면 같은 원소를 중복 계산 • 계산 시간이 지수함수적으로 증가 (가장 단순한 예: 피보나치 수의 재귀적 계산) • 큰 문제가 작은 문제를 포함 • 작은 문제를 풀어두면 큰 문제를 풀 수 있다 • 테이블을 계산해두면 비슷한 문제들을 풀 때 재활용 가능 • 메모리를 희생하고 속도를 얻는다
  • 20. 하스켈식 해법 1 divisors n = [ x | x <- [1..(n-1)], n `mod` x == 0] 2 3 knapsack n divs = table where 4 table :: Int -> Int -> Int 5 table 0 j = 0 6 table i j 7 | si > j = table (i-1) j 8 | otherwise = maximum [ table (i-1) j, table (i-1) (j-si) + si ] 9 where si = divs !! i 10 11 isWeird n = abundant && not semiperfect where 12 divs = divisors n 13 abundant = sum divs > n 14 semiperfect = (knapsack n divs) (length divs - 1) n == n 15 16 main = do 17 putStrLn $ "is 12 weird?: " ++ show (isWeird 12) 18 putStrLn $ "12 70 weird?: " ++ show (isWeird 70)
  • 21. 하스켈식 해법 1 divisors n = [ x | x <- [1..(n-1)], n `mod` x == 0] 2 3 knapsack n divs = table where 4 table :: Int -> Int -> Int 5 table 0 j = 0 6 table i j 7 | si > j = table (i-1) j 8 | otherwise = maximum [ table (i-1) j, table (i-1) (j-si) + si ] 9 where si = divs !! i 10 11 isWeird n = abundant && not semiperfect where 12 divs = divisors n 13 abundant = sum divs > n 14 semiperfect = (knapsack n divs) (length divs - 1) n == n 15 16 main = do 17 putStrLn $ "is 12 weird?: " ++ show (isWeird 12) 18 putStrLn $ "12 70 weird?: " ++ show (isWeird 70) 점화식 중복 계산
  • 22. 진짜 하스켈식 해법 • 테이블 자체를 재귀적으로 정의하면 안된다 • 테이블의 원소를 재귀적으로 정의하면 된다 • ??? knapsack n divs len = table where table = array ((0,0),(len,n)) $! [((0,w),0) | w <- [0..n]] ++ [((i,w),v) | i<-[1..len], w<-[0..n], let v = maximum [at(i-1,w), divs!i + at(i-1,w-(divs!i))]] at (i, w) = if w < 0 then -n else table ! (i,w)
  • 23. 메모이제이션(MEMOIZATION) • 테이블 계산하는 게 메모이제이션 • 하스켈에서는 테이블의 원소를 재귀적으로 정의해도 메모이제이션이 가능하다 • 지연 평가(Lazy Evaluation) • 데이터 불변성(Data Immutability) • 고정점 연산자(Fixed Point Operator)
  • 24. 성능을 측정해보자 • 문제에서 주어진 조건은 1 <= N <= 500000 • N = 500000에 대해 테스트
  • 25. 무엇이 문제인가 • 지연 평가 • 데이터타입 • 알고리즘 자체의 문제
  • 26. 무엇이 문제인가 – 지연 평가 • 값이 실제로 필요하기 전에는 계산하지 않는다 • 1에서 N까지 더하기 • foldr (+) 0 [1..N] • 메모리 복잡도는 O(1)? • foldr (+) 0 [1..3] = 1 + (foldr (+) 0 [2..3]) = 1 + (2 + foldr (+) 0 [3]) = 1 + (2 + (3 + (foldr (+) 0 []))) = 1 + (2 + (3 + 0)) = 1 + (2 + 3) = 1 + 5 = 6 • 메모리 복잡도가 O(N) • 핵심: 표현식이 한없이 길어진다 이 계산 트리가 완성된 후 계산 시작
  • 27. 무엇이 문제인가 – 데이터타입 • C언어 • int n; • 4바이트 • 하스켈 • n :: Int • data Int = I# Int# • 2워드 = 8바이트 • 박싱(boxing), 언박싱(unboxing) 필요 대응
  • 28. 무엇이 문제인가 – 애초에 알고리즘이 구려 • 테이블로 돌아가보자 • I번 행을 계산하는 데 (i-1)번 행만 필요 – 전체 테이블을 유지할 필요가 없다 N = 12 1 2 3 4 5 6 7 8 9 10 11 12 s0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1 s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3 s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6 s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10 s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
  • 29. 해결 방법 • 철저한 평가(Strict Evaluation) • 표현식이 너무 길어지기 전에 계산을 강제로 수행 • 원시 타입(Unboxed type) • 정수 표현에 C와 같은 바이트 사용 • 테이블의 두 행만 메모리에 유지 • 필요없는 행은 바로 가비지 컬렉션이 되도록
  • 30. 해결 방법 knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where a prevRow prevA | prevRow == len = prevA | True = currA `seq` a (prevRow+1) currA where currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n], let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]] at w = if w < 0 then -n else prevA ! w
  • 31. 해결 방법 knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where a prevRow prevA | prevRow == len = prevA | True = currA `seq` a (prevRow+1) currA where currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n], let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]] at w = if w < 0 then -n else prevA ! w 철저한 평가
  • 32. 해결 방법 knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where a prevRow prevA | prevRow == len = prevA | True = currA `seq` a (prevRow+1) currA where currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n], let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]] at w = if w < 0 then -n else prevA ! w Unboxed 타입
  • 33. 해결 방법 knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where a prevRow prevA | prevRow == len = prevA | True = currA `seq` a (prevRow+1) currA where currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n], let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]] at w = if w < 0 then -n else prevA ! w 일부 행만 유지
  • 34. 성능을 측정해보자 • 다시 N = 500000에 대해 테스트 • 메모리 사용 1/1000 • 속도 2.5배
  • 36. 추가 자료 • 고성능 하스켈 • http://www.slideshare.net/tibbe/highperformance-haskell • 하스켈 퍼포먼스 • https://www.youtube.com/watch?v=_pDUq0nNjhI • 하스켈 동시성 & 병렬 프로그래밍 • http://chimera.labs.oreilly.com/books/1230000000929/index.html