최적화와 수학

애자일 블로그에 올라온 프로그래머와 수학을 읽고 전에 경험했던 것을 짧게 써 봅니다.

프로그램을 하다보면, 뭔가 하고 있는 것이 비효율적인 것 같은 느낌이 약간은 들지만, 최적화가 충분히 되어있다고 느낄 때가 많습니다. 더 속도가 필요하다면, 더 빠른 언어와 여러 트릭을 동원해서 빠르게 하곤 합니다. 극단적인 경우를 예를 들어서, 항상 예제로 많이 쓰이는 피보나치 수열을 한 번 보겠습니다. 실제 프로그래밍이 피보나치 수열처럼 간단한 루틴일 리도 없고 그렇게 반복적으로 호출될 일은 없겠지만, 그래도 극단적인 예를 보면, 그 특징을 다른 곳에서 활용할 수 있을테니까요~

일반적으로 피보나치 수열 n번째를 구하는 루틴은 파이썬으로 이렇게 합니다.

만약 피보나치 수열의 200005번째 수 “주변”의 것이 정확할 필요는 없고 대충의 값이 필요해서 호출한다면, 제 컴퓨터에서는 대충 2.82초 정도가 걸립니다. 그런데, 이런 루틴이 적어도 초당 100번 이상은 호출되어야 하는 상황이 왔다고 치면 보통 이런 대안을 생각합니다.

  • 파이썬이 느리니까 C로 하자!
  • a, b는 터플이 묶였다가 풀리면서 메모리 할당을 하니까 따로 따로 하게 2줄로 분리해 보자.
  • 내가 요새 어셈블리에 재미를 붙였는데, 어셈블리로 하면 레지스터도 충분히 활용하고 빠르지 않을까?
  • 루프를 매번 돌리는 것은 비효율적이니까, 16번에 한번씩 돌게 풀어쓰면(unroll) 빨라질거야!

자.. 그런데, C로 하면 과연 얼마나 빨라질까요. 200005번째 피보나치 수는 64비트로 커버가 안 되는 수이기 때문에, 빅넘버 라이브러리를 쓰다보면 결국 시간이 제법 걸리고, 속도도 생각보다는 별로 빠르지 않습니다. 어셈블리로 하면? 과연 오늘 퇴근 할 수 있을까요..

여기서 경험많은 프로그래머는 알고리즘을 휙 보고, 이런 코드로 최적화를 합니다.

200005번째 수를 주는데 걸리는 시간이 눈깜짝할 새 입니다. 아이 그런데, 또 주변의 수가 더 필요해졌습니다. 그래서, 좀 더 많은 범위에서 캐시를 할 수 있게 데코레이터도 쓰고 해서 멋있는 캐시를 넣어서 정말 더 빨라졌습니다. 그랬더니 이제는 메모리를 너무 많이 먹습니다! 결국에는 캐시 관리 기능에 메모리가 얼마인지 보게도 만들어서, 이 프로그래머는 주변의 존경을 받습니다.

그러나, 이때 옆에서 보던 한 수학자가 종이에 뭘 끄적거리더니, 이렇게 하면 안 되겠냐고 조심스럽게 코드를 한손가락으로 톡톡 쳐줍니다.

아! 아.. 프로그래머는 뭔가 속은 기분이 들었습니다. 그래서 유심히 보니까, 실행해 보면 정확한 값이 나오지는 않았습니다. 그래서 따져보고 싶었지만 필요한 것은 그냥 대충 화면 좌표를 잡는 것이라 유효숫자 6개 정도만 하면 넘칠정도라는 사실에 슬펐습니다.

컴퓨터 전공 학생들은 대부분 4학년 되면, 적분기호를 까먹을 정도로 수학을 쓸 일이 없습니다. 그런데, 간단한 프로그래밍의 수준을 지나면 생각보다 훨씬 수학을 활용할 수 있는 일이 늘어나는 것 같습니다. 물론 굳이 수학 뿐만 아니라, 넓게 봐서 논리의 흐름과 여러가지 알고리즘적인 방법, 패턴 등이 있겠죠.

모니터 가까이에서 코드를 보고 있으면, 앞에서 본 C로 하면 무조건 제일 빠를거야!, 터플 때문에 괜히 메모리 할당하는 것을 줄이면 정말 빠를거야! 라는 생각을 늘 하게 되기에 마련이지만, 가끔은 좀 멀리서 최적화에 조바심을 내지 않으면서 코드를 볼 기회도 가져봄 직 할 것 같습니다.

“Real efficiency comes from elegant solutions, not optimized programs.
Optimization is always just a few correctness-preserving transformations
away.” – Jonathan Sobel, 《Is Scheme Faster than C?》에서

붙임: 중간에 든 예가 좀 과장이 심해서 죄송 -ㅇ-; 글의 원래의도가 피보나치 수열을 최적화하자는 것은 아닙니다. ^^;

12 thoughts on “최적화와 수학”

  1. 위에서 수학자가 풀어주었다는 게 점화식을 특성방정식으로 풀어서 a(n)을 바로 구하는 건가요?
    소스가 잘 안 읽혀서…;;

  2. 저라면 2×2 행렬로 피보나치 식을 만들고, 자승을 구해서~ ^^
    200000 정도라면 가볍게 18번의 이터레이션으로~ ‘ㅁ’

  3. Jiee: 예 맞습니다. -O-; 알기 쉽게 쓰려고 썼는데 전달이 잘 안 돼서 부끄럽습니다. -ㅇ-;

    JM: 훈련가신다더니~ ^^ 한동안 RSS 에러가 나길래 (planet yonsei cs), 벌써 가신 줄 알았~

  4. 흐.. 예전에 1부터 n까지 합을 구하는 소스를 비슷한 형식(당연히 for사용 vs n(n+1)/2 리턴)으로 전개한 글을 본적이 있어서 글의 결말을 바로 예측해 버리고 말았습니다 ㅡ.ㅜ

  5. 저 놈의 recurrence relation은 잊고 싶어도 모 과목의 압박 때문에 잊을 수 없어요. orz

  6. JM // 피보나치 수열을 2-by-2 행렬로 만든다음에 eigenvalue를 구해서 원하는 값을 구해도 좋을 것 같네요. ^^

  7. 그 수학자 실력이 별로군요. 저 같으면

    return int( (d1/r5) * (( (d1+r5)/d2 )**(n+d1) ) + d(‘.5’) )

    로 하겠습니다. ^^

  8. [등장인물]
    – 일반 프로그래머 : perky
    – 경험 많은 프로그래머 : perky
    – 존경받는 프로그래머 : perky
    – 옆에서 보던 한 수학자 : perky
    ^O^//=3=33

  9. 그나저나 점화식, 특성방정식…참 오랜만에 들어보네요. 외계어같기도 하고^^=3=33

Comments are closed.