Повышение скорости передачи данных из Python в C (++) через ctypes.

Мне нужно оптимизировать вызов функции, который находится в цикле, для критического по времени приложения робототехники. Мой скрипт написан на python, который через ctypes взаимодействует с библиотекой C ++, которую я написал, которая затем вызывает библиотеку микроконтроллеров.

Узким местом является добавление точек положения-скорости-времени в буфер микроконтроллера. Согласно моим проверкам синхронизации, вызов функции C ++ через ctypes занимает около 0.45 секунд и на стороне C ++ вызываемая функция принимает 0.17 секунд. Мне нужно как-то уменьшить эту разницу.

Вот соответствующий код Python, где данные — это двумерный массив точек, а библиотека загружается через ctypes:

data_np = np.vstack([nodes, positions, velocities, times]).transpose().astype(np.long)

data = ((c_long * 4) * N)()
for i in range(N):
data[i] = (c_long * 4)(*data_np[i])

timer = time()
clibrary.addPvtAll(N, data)
print("clibrary.addPvtAll() call: %f" % (time() - timer))

А вот так называемая функция C ++:

void addPvtAll(int N, long data[][4]) {

clock_t t0, t1;
t0 = clock();

for(int i = 0; i < N; i++) {
unsigned short node = (unsigned short)data[i][0];
long p = data[i][1];
long v = data[i][2];
unsigned char t = (unsigned char)data[i][3];

VCS_AddPvtValueToIpmBuffer(device(node), node, p, v, t, &errorCode);
}

t1 = clock();
printf("addPvtAll() call: %f \n", (double(t1 - t0) / CLOCKS_PER_SEC));
}

Мне не обязательно использовать ctypes, но я не хочу компилировать код Python каждый раз, когда я его запускаю.

3

Решение

Обратный путь между Python и C ++ может быть дорогим, особенно при использовании ctypes (который похож на интерпретированную версию обычной оболочки C / Python).

Ваша цель должна состоять в том, чтобы свести к минимуму количество поездок и сделать максимально возможную работу за поездку.

Мне кажется, что ваш код имеет слишком тонкую гранулярность (то есть делает слишком много поездок и выполняет слишком мало работы в каждой поездке).

NumPy Пакет может предоставлять свои данные напрямую C / C ++. Это позволит вам избежать дорогостоящей упаковки и распаковки объектов Python (с выделенным для них выделением памяти) и позволит вам передавать диапазон точек данных, а не точку за один раз.

Модифицируйте свой код C ++, чтобы обрабатывать много точек одновременно, а не один раз за вызов (очень похоже на sqlite3 модуль делает с выполнять против executemany).

1

Другие решения

Вот мое решение, которое эффективно устраняет измеренную разницу во времени между Python и C. Благодарю kirbyfan64sos за предложение SWIG и Raymond Hettinger для C-массивов в numpy. Я использую пустой массив в Python, который отправляется на C просто как указатель — к одному и тому же блоку памяти обращаются на обоих языках.

Функция C остается идентичной, за исключением использования gettimeofday() вместо clock(), который давал неточные времена:

void addPvtFrame(int pvt[6][4]) {

timeval start,stop,result;
gettimeofday(&start, NULL);

for(int i = 0; i < 6; i++) {
unsigned short node = (unsigned short)pvt[i][0];
long p = (long)pvt[i][1];
long v = (long)pvt[i][2];
unsigned char t = (unsigned char)pvt[i][3];

VCS_AddPvtValueToIpmBuffer(device(node), node, p, v, t, &errorCode);
}

gettimeofday(&stop, NULL);
timersub(&start,&stop,&result);
printf("Add PVT time in C code: %fs\n", -(result.tv_sec + result.tv_usec/1000000.0));
}

Кроме того, я установил SWIG и включил в свой файл интерфейсов следующее:

%include "numpy.i"%init %{
import_array();
%}

%apply ( int INPLACE_ARRAY2[ANY][ANY] ) {(int pvt[6][4])}

Наконец, мой код Python конструирует pvt как непрерывный массив через NumPy:

pvt = np.vstack([nodes, positions, velocities, times])
pvt = np.ascontiguousarray(pvt.transpose().astype(int))

timer = time()
xjus.addPvtFrame(pvt)
print("Add PVT time to C code: %fs" % (time() - timer))

Измеренные времена теперь имеют примерно% 1 разницы на моей машине.

0

Вы можете просто использовать data_np.data.tobytes():

data_np = np.vstack([nodes, positions, velocities, times]).transpose().astype(np.long)
timer = time()
clibrary.addPvtAll(N, data_np.data.tobytes())
print("clibrary.addPvtAll() call: %f" % (time() - timer))
0
По вопросам рекламы [email protected]