카테고리 없음

[파이썬/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]