파이썬으로 C를 컴파일하다…사이썬과 다른 길을 가는 파이토씨
컨텐츠 정보
- 조회 425
본문
파이토씨(PythoC)는 파이썬을 C 코드 생성기로 사용할 수 있게 해주면서 기능과 유연성 측면에서도 사이썬(Cython)보다 더 뛰어나다. 파이썬을 위한 새로운 C 코드 생성기를 살펴보자.
파이썬과 C는 겉으로 보이는 것보다 많은 부분을 공유한다. 파이썬 인터프리터 레퍼런스 버전은 C로 작성됐으며, 파이썬용으로 제작된 서드파티 라이브러리 중 상당수는 C 코드를 래핑한다. 또한 파이썬에서 C 코드를 생성하는 것도 가능하다.
파이썬으로 C 코드를 생성하기 위한 전통적인 방법은 사이썬과 같은 라이브러리를 통한 방법인데, 이 경우 타입 어노테이션이 달린 파이썬 코드를 사용해 파이썬을 위한 C 확장 모듈을 생성한다.
그런데 파이토씨(PythoC)라는 새로운 프로젝트의 접근 방식은 다르다. 파이토씨는 타입 힌트가 달린 파이썬을 사용해 프로그래밍 방식으로 C 코드를 생성한다. 주로 독립 실행을 목적으로 하며, 컴파일 타임 코드 생성 기능이 사이썬에 비해 훨씬 많다.
파이토씨 개발진은 이 접근 방식을 설명하기 위해 “C 레벨 런타임, 파이썬 기반 컴파일 타임”이라는 표현을 사용한다. 프로젝트는 아직 초기 단계지만 여러 기능이 이미 충분히 작동하므로 살펴볼 가치가 있다.
기본적인 파이토씨 프로그램
다음은 파이토씨에서 제공하는 예제를 기반으로 만든 간단한 프로그램이다.
from pythoc import compile, i32@compiledef add(x: i32, y: i32) -> i32: return x + yif __name__ == "__main__": print(add(10, 20))
모듈에서 어느 함수를 C로 컴파일할지 가리키려면 파이토씨의 @compile 데코레이터를 사용하고 결과와 각 매개변수에 대한 타입 힌트를 제공한다. 참고로 파이썬의 네이티브 int가 아니라 파이토씨 자체의 i32 힌트를 가져와야 한다. 즉, 파이썬의 임의 크기 int가 아닌 머신 네이티브 정수를 사용한다.
이 프로그램을 실행하면 얼마간의 지연 후 30이 출력으로 나온다. 지연이 발생하는 이유는 프로그램을 실행할 때마다 C 코드가 즉석에서 컴파일되기 때문이다. 파이토씨는 사이썬과 달리 파이썬에서 호출될 때 컴파일된 코드를 재사용하는 메커니즘이 아직 없다.
처음에는 상당히 큰 제약으로 보이는데 사실 여기에 핵심이 있다. 즉, 파이토씨는 파이썬으로 가져오는 C 모듈이 아니라 독립적으로 실행되는 C 프로그램을 위한 코드 생성 시스템으로 사용할 수 있다는 것이다.
독립형 C 프로그램 생성
다음은 동일한 프로그램이지만 동작이 다른 새로운 버전이다.
from pythoc import compile, i32, ptr, i8from pythoc.libc.stdio import printf@compiledef add(x: i32, y: i32) -> i32: return x + y@compiledef main(argc: i32, argv: ptr[ptr[i8]]) -> i32: printf("%un", add(10, 20))if __name__ == "__main__": from pythoc import compile_to_executable compile_to_executable()아마 맨 아래 블록이 가장 먼저 눈에 띌 것이다. compile_to_executable() 함수는 이름 그대로다. 호출하면 현재 모듈이 동일한 이름의 실행 파일로 컴파일되며 @compile 데코레이터가 붙은 모든 함수가 포함된다.
또 다른 차이는 main() 함수가 이제 C 프로그램의 main() 함수와 동일한 시그니처를 갖는다는 점이다. 즉, 컴파일된 실행 파일이 자동으로 이 함수를 진입점으로 사용한다.
참고로 이 프로그램을 실행하면 생성된 실행 파일(build 하위 디렉터리에 위치)이 자동으로 실행되지는 않으므로 수동으로 실행해야 한다. 여기서 목표는 개발자가 직접 C로 작성한 것과 구분되지 않을 정도의 독립형 C 프로그램을 만들되 파이썬의 구문을 사용하는 것이다.
파이토씨의 C 기능 에뮬레이션
일부 예외는 있지만 파이토씨는 C의 기능 집합과 런타임을 완전히 활용하는 코드를 생성할 수 있다.
타입 아노테이션을 사용해서 원시 데이터 타입을 나타내는 방법은 위에서 이미 살펴봤다. 마찬가지로, ptr[T] 어노테이션을 사용해 포인터를 나타낼 수 있고(마찬가지로 위에서 볼 수 있음), T 타입의 N차원 배열에는 array[T, N]을 사용할 수 있다. 파이썬 클래스에 데코레이터를 사용해서 구조체, 공용체, 열거형을 만들 수 있고 goto를 제외한 모든 일반적인 연산자와 제어 흐름 연산이 작동한다. switch/case의 경우 match/case를 사용하면 되지만 폴스루(fall-through) 케이스는 사용할 수 없다.
누락된 또 다른 요소는 가변 길이 배열이다. 다만 이 기능이 C에서도 C11 이후에만 지원되고 컴파일러에서의 지원 여부는 선택 사항임을 감안하면 파이토씨가 아직 지원하지 않는다고 해서 이상할 것은 없다.
컴파일 타임 코드 생성
컴파일 타임 코드 생성에 사이썬을 사용하는 것도 가능하다. 즉, 컴파일 타임에 어떤 일이 일어나는지에 따라 다양한 종류의 C 코드를 생성하거나 파이썬 코드로 되돌아갈 수 있다. 그러나 파이토씨의 컴파일 타임 코드 생성에는 사이썬에 없는 기능이 있다.
파이토씨 설명서에서 가져온 다음 예제를 보자.
from pythoc import compile, struct, i32, f64def make_point(T): @struct(suffix=T) class Point: x: T y: T @compile(suffix=T) def add_points(p1: Point, p2: Point) -> Point: result: Point = Point() result.x = p1.x + p2.x result.y = p1.y + p2.y return result return Point, add_pointsPoint_i32, add_i32 = make_point(i32)Point_f64, add_f64 = make_point(f64)
make_point(T) 함수는 타입 어노테이션(i32, f64)을 취하고 컴파일 타임에 Point 클래스와 add_points 함수의 타입 특화 버전을 생성한다. @compile의 suffix 매개변수는 “생성된 객체의 이름을 변경해서 이름 안에 타입이 사용되도록 하라”는 의미다. 예를 들어 Point는 Point_i32와 Point_i64가 되는데, 이는 C에서 타입 시그니처가 서로 다른 동일한 함수의 여러 버전을 구분하는 방법 중 하나다. 또한 런타임 디스패치와 함께 사용해서 다형성을 제공할 수도 있다
메모리 안전 기능
C의 수동 메모리 관리에서 비롯되는 버그는 C 언어를 사용하는 누구에게나 불쾌하게 익숙한 문제다. 사이썬도 이 문제를 해결하기 위한 메모리 안전 기능을 제공하지만 파이토씨는 독자적인 타입 기반 기능을 제공한다.
그중 하나는 선형 타입(linear types)이라는 기능이다. linear 가져오기를 통해 “증명(일반적으로 메모리 할당과 함께 사용됨)”을 생성할 수 있으며 이 증명은 동일한 메모리가 할당 해제될 때 “소비”돼야 한다. 각 prf=linear()에 대해 상응하는 consume(prf)이 없으면 파이토씨 컴파일러에서 컴파일 타임 오류가 발생한다. 위에 링크된 관련 문서에서 간단한 lmalloc()/lfree() 함수를 만들어 안전하게 메모리를 할당하고 해제하는 방법을 볼 수 있다. 반드시 malloc()/free()를 사용한 수동 방식이 아닌 선형 타입을 사용해야 하는 것은 아니지만 선형 타입은 수동 검사의 상당부분을 자동화하고 런타임이 아닌 컴파일 타임에 중앙 집중화할 수 있다.
또 다른 타입 기반 안전 기능은 정제 타입(refinement type)이다. 이 개념은 예를 들어 널 포인터 검사를 수행하고 부울 결과를 반환하는 함수와 같이 특정 검사를 수행하는 함수를 정의할 수 있다는 것이다. 여기서 refine() 함수를 사용해 값을 해당 함수에 전달하면 refined[func]라는, 그 함수에 특화된 타입을 돌려받게 된다. 이를 통해 컴파일러는 해당 타입이 반환되기 전에 어떤 방식으로든 처리되도록 보장하고, 일반적인 검사(널 포인터 검사 등)를 코드 내의 한 곳에서 처리할 수 있다. 사이썬의 타입 시스템은 C 동작을 직접 에뮬레이션하는 데 주 목적을 두는 만큼 이와 같은 기능은 제공하지 않는다.
파이토씨의 미래
파이토씨는 여전히 매우 초기 단계의 프로젝트이고 미래의 개발 방향도 비교적 열려 있다. 한 가지 가능성은 런타임에서 파이썬과 더 밀접하게 통합되는 것이다. 예를 들어 @cached 데코레이터를 사용해서 모듈을 한 번만 사전에 컴파일하고, 이후 파이썬에서 호출될 때마다 다시 컴파일하지 않고 재사용할 수 있게 될 수도 있다. 물론 이를 위해서는 기존 파이썬 모듈 빌드 시스템과의 통합이 필요하다. 이 정도 수준의 통합은 파이토씨 프로젝트의 목표를 벗어날 수도 있지만, C와 파이썬을 통합하는 사용자에게 파이토씨의 유용성을 즉각 높여줄 것이다.
dl-itworldkorea@foundryco.com
관련자료
-
링크
-
이전
-
다음






