#python #c #numpy #cffi
#python #c #numpy #cffi
Вопрос:
Я пытаюсь ускорить свою программу на Python, реализовав функцию на C и внедрив ее в свой код с использованием CFFI. Функция принимает два массива размером 3×3 и вычисляет расстояние. Код Python выглядит следующим образом:
import cffi
import numpy as np
ffi = cffi.FFI()
ffi.cdef("""
extern double dist(const double s[3][3], const double t[3][3]);
""")
lib = ffi.dlopen("./dist.so")
S = np.array([[-1.63538, 0.379116, -1.16372],[-1.63538, 0.378137, -1.16366 ],[-1.63193, 0.379116, -1.16366]], dtype=np.float32)
T = np.array([[-1.6467834, 0.3749715, -1.1484985],[-1.6623441, 0.37410975, -1.1647063 ],[-1.6602284, 0.37400728, -1.1496595 ]], dtype=np.float32)
Sp = ffi.cast("double(*) [3]", S.ctypes.data)
Tp = ffi.cast("double(*) [3]", T.ctypes.data)
dd = lib.dist(Sp,Tp);
Это решение работает не так, как предполагалось. Действительно, аргументы, печатаемые функцией C, являются:
Sp=[[0.000002, -0.270760, -0.020458]
[0.000002, 0.000000, 0.000000]
[0.000000, 0.000000, 0.000000]]
Tp=[[0.000002, -0.324688, -0.020588]
[0.000002, 0.000000, 0.000000]
[0.000000, 0.000000, -nan]]
Я также попробовал следующее для инициализации указателей:
Sp = ffi.new("double *[3]")
for i in range(3):
Sp[i] = ffi.cast("double *", S[i].ctypes.data)
Tp = ffi.new("double *[3]")
for i in range(3):
Tp[i] = ffi.cast("double *", T[i].ctypes.data)
dd = lib.dist(Sp,Tp);
Но это решение вызывает ошибку в dist(Sp,Tp)
:
TypeError: initializer for ctype 'double(*)[3]' must be a pointer to same type, not cdata 'double *[3]'
У вас есть какие-либо идеи о том, как заставить это работать? Спасибо.
Ответ №1:
Типы double[3][3]
и double *[3]
не эквивалентны. Первый представляет собой 2D-массив double 3×3, хранящийся как 9 непрерывных double. Последний представляет собой массив из 3 двойных указателей, который не является тем, как 2D статические массивы реализованы в C или C .
Как массивы numpy, так и статические массивы C представлены в памяти в виде непрерывного блока элементов, это просто double *[3]
тип в середине, который бросает гаечный ключ в работу. Что бы вы хотели, так это использовать double[3][3]
правильное или double[3]*
(указатель на строку из трех двойников). Обратите внимание, что если вы используете последнее, вам может потребоваться изменить прототип функции на take double [][3]
.
Комментарии:
1. Спасибо. Действительно, приведение к
double[3][3]
сработало. В любом случае мне также пришлось изменить тип numpy наfloat64