카테고리 없음
[파이썬/Ctypes] Numpy 1차원 배열을 C함수로 넘기기
미친토끼
2021. 3. 11. 14:38
넘파이 행렬을 Ctypes을 통해 C 함수로 넘길 수는 없을까?
1차원 배열의 경우는 아래와 같이 간단하게 처리된다.
// numpy_0.c
// numpy로 받은 1차원 배열을 읽어 다른 배열에 저장한다.
// linux 64bit에서 double은 8바이트로, 넘파이의 float64에 해당된다.
// 넘파이에서 데이터 생성 시, dtype=np.float64를 명시해줘야 한다.
void numpy_0 (double * src, double * dst, int length) {
for (int i = 0; i < length; i++) {
dst[i] = src[i] * 3;
}
return;
}
int 자료형 크기는, 파이썬과 리눅스에서 서로 다를 수 있다.
필자도 테스트하다가 이상한 결과가 나와서 int 자료형을 살펴본 결과,
리눅스는 당연히도 4바이트였고 파이썬으로 치면 int32인데, 파이썬 넘파이에서는 int64가 할당되어 있더라.
C에서 처리해서 파이썬으로 되돌린 결과가 예를 들면, [1, 2, 3, 4, 5] 이렇게 나와야 하는데 [1, 쓰레기, 2, 쓰레기, 3, 쓰레기, 4, 쓰레기, 5, 쓰레기] 이런 식으로 저장되어 있더라. 즉, python에서 64비트 int자료형 5개를 넘겨줬는데 c에서 그것을 32비트로 이해하고 처리했으니 이상한 결과가 나온 것이다.
#!/usr/bin/env python3
import ctypes
import numpy as np
# float64형 데이터 4개 생성하여 넘파이 배열에 담음.
data = np.array([0.1, 0.2, 0.3, 0.4], dtype=np.float64)
# 원 배열을 복사하여 다른 배열을 만든다.
dst_data = data.copy()
# double 포인터 자료형을 정의한다
c_double_p = ctypes.POINTER(ctypes.c_double)
# double형 포인터 변수에 원본 데이터의 주소를 받는다.
data_p = data.ctypes.data_as(c_double_p)
# 복사본 데이터의 주소도 double형 포인터로 받아둔다.
dst_data_p = dst_data.ctypes.data_as(c_double_p)
# 다이나믹 라이브러리를 로딩한다.
# 당황스럽게도 리눅스용 IDE Spyder에서는 다이나믹 라이브러리를 한번 로딩했다면
# 다음에는 로딩하지 않고 캐싱하는 것 같다. 역시 command line에서 실행하는 게 최고...
c_lib = ctypes.CDLL("./numpy_0.so")
c_lib.numpy_0(data_p, dst_data_p, len(data))
# python에서 출력할 때는 C를 위해 만들어둔 ctypes 포인터 변수를 사용하지 않고, 원래 만들어둔 배열을 사용한다.
print(data)
print(dst_data)
[실행 결과]
[0.1 0.2 0.3 0.4]
[0.3 0.6 0.9 1.2]