본문 바로가기

Computer/Python

파이썬 코드 C언어로 컴파일하기 (2)

반응형

파이썬 코드 C언어로 컴파일하기 (1)


지난 포스트에서 calculate_z 함수를 사이썬으로 빌드하여 속도 개선을 얻었지만 calculate_z 함수 내용을 아예 건드리지 않고 그대로 빌드했습니다. 그렇다면 무엇을 어떻게 변경하면 추가적인 속도 개선을 얻을 수 있을까요?

C언어와 파이썬의 여러 차이점 중 하나는 C언어는 변수의 타입을 미리 지정하는 정적언어인데 반해 파이썬은 변수가 어떤 타입이라도 참조할 수 있고 코드 어디에서든 참조하는 객체의 타입을 변경할 수 있는 동적언어라는 점입니다. 따라서 순수 파이썬으로 작성된 코드는 동적언어라는 특징 때문에 가상 머신에서 다음 연산에 사용할 기본적인 데이터 타입을 알 수 없으므로 기계어 수준의 최적화를 수행하기 어렵습니다. 실행 속도 대신 개발 속도를 선택한 파이썬의 숙명이죠. 그렇다면, CPU를 많이 사용하면서 변수의 타입을 바꾸지 않는 코드에 대해서 미리 컴파일하면 속도를 더 개선할 수 있지 않을까요? 

고수준, 추상화된 파이썬 객체는 파이썬 가상 머신에서 실행되므로 이 부분을 C객체로 변환하고자 합니다. 이 부분은 계산이 끝난 후에 다시 파이썬 객체로 변환됩니다. "cdef" 키워드를 이용하면 함수 내에서 변수를 선언할 수 있고 C언어와 마찬가지로 함수 시작 부분에서 변수를 다음과 같이 선언합니다.

# cpythonfn.pyx
def calculate_z(int maxiter, zs, cs):
    cdef unsigned int i, n
    cdef double complex z, c
    output = [0] * len(zs)
    for i in range(len(zs)):
        n = 0
        z = zs[i]
        c = cs[i]
        while abs(z) < 2 and n < maxiter:
            z = z*z + c
            n += 1
        output[i] = n
    return output
  • 여기서 지정한 타입은 파이썬이 아니라 사이썬에서 해석합니다. 사이썬은 이 타입 정보를 이용하여 파이썬 코드를 C객체로 변환해서 파이선 스택에서 호출되지 않도록 합니다.

이를 setup.py 파일을 이용해 다시 빌드하고 julia 프로그램을 실행하면 0.12~0.13초 정도 밖에 걸리지 않습니다. 기본적인 C타입을 지정하여 파이썬 가상 머신보다 C코드로 더 많이 동작하도록 변경함으로써 속도를 거의 20배 정도 빠르게 향상시켰습니다. ( $z,n$과 같이 값이 자주 갱신되는 부분을 C코드 수준에서 동작하도록 변경됐기 때문입니다) 물론, 실행 속도는 빨라지지만 1) 코드 작성 - 2) 컴파일 - 3) 실행 순으로 동작하기에 프로그램의 유연성, 개발 속도 저하 등은 감수해야겠죠.

>>> python julia1.py
Total elements: 1000000
calculate_z  took 0.1200404167175293 seconds

 

추가적인 최적화

파이썬의 abs 내장 함수는 타입에 따라 다르게 동작합니다. 정수나 실수에 사용하면 단순히 음수를 양수로 변환하고 복소수는 실수와 허수의 제곱의 합의 제곱근을 반환합니다. 따라서 complex 변수에 대해서 더 많은 오버헤드가 걸리게 되는데, 파이썬은 abs를 호출하기 전에 먼저 해당 변수의 타입을 살펴보고 어떤 버전의 함수를 호출할지 결정하기에 이 호출 횟수가 많을수록 오버헤드는 증가합니다.

따라서 이 부분도 제곱근 (sqrt)을 하는 대신 제곱 연산을 그대로 유지하는 방향으로 코드를 변경하면 C객체화된 $z$를 사용하면서 연산량 또한 더 줄일 수 있습니다.

    for i in range(len(zs)):
        n = 0
        z = zs[i]
        c = cs[i]
        while (z.real*z.real + z.imag*z.imag) < 4 and n < maxiter:
            z = z*z + c
            n += 1
        output[i] = n
  • 컴파일해서 실행하면 0.11초 정도가 나오는데, 크게 개선이 되지는 않았네요.

 


파이썬 코드 C언어로 컴파일하기 (3) - 사이썬과 넘파이

반응형