Синхронизация функции C ++, обернутой с помощью Cython - PullRequest
0 голосов
/ 29 мая 2018

Я действительно не знаю, как задать этот вопрос, но я постараюсь быть максимально ясным.

Я рассчитываю вызов функции C ++ из python.Функция C ++ обернута с помощью Cython.В настоящее время я синхронизирую вызов python функции cython и получаю 52,9 мс с time.time().С другой стороны, я синхронизирую всю функцию C ++ с библиотекой C ++ std::chrono::high_resolution_clock.

Дело в том, что я измеряю 17,1 мс в C ++.

Функция C ++ объявлена ​​какthis vector<float> cppfunc(vector<float> array, int a, int b, int c); и является методом класса A.

Код Cython вызывает только метод класса C ++.Вектор содержит около 320 тыс. Элементов.

Мне было интересно, можно ли сравнить эти два измеренных времени?Если мы можем, что может объяснить этот пробел?Если нет, какой инструмент синхронизации мне следует использовать?

Edit1: (ссылки в комментариях) обе библиотеки синхронизации достаточно точны для моего варианта использования (10e-9 для cpp на моемarch и 10e-6 для python).

Edit2: добавлен упрощенный код, чтобы проиллюстрировать мою точку зрения.С этим кодом длительность вызова Python (~ 210 мс) в 8 раз превышает длительность интерна cpp (~ 28 мс).

// example.cpp
#include "example.h"
#include <iostream>
#include <chrono>

std::vector<float> wrapped_function(std::vector<float> array)
{
    auto start = std::chrono::high_resolution_clock::now();
    std::vector<float> result;
    for (int i = 0; i < (int) array.size(); i++) {
        result.push_back(array[i] / 1.1);
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<float> duration = end - start;
    printf("Within duration: %.5f\n", duration.count());
    return result;
}


// example.h
#ifndef __EXAMPLE_H_
#define __EXAMPLE_H_
#include <vector>
std::vector<float> wrapped_function(std::vector<float> array);
#endif


# example_wrapper.pxd
from libcpp.vector cimport vector
cdef extern from "example.h":
    vector[float] wrapped_function(vector[float])


# example_wrapper.pyx
from example_wrapper cimport wrapped_function
def cython_wrap(array):
    return wrapped_function(array)


# setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
setup(
      cmdclass = {"build_ext": build_ext},
      ext_modules = [
            Extension(name="example_wrapper",
                      sources=["example_wrapper.pyx", "example.cpp"],
                      include_dirs=["/home/SO/"],
                      language="c++",
                      extra_compile_args=["-O3", "-Wall", "-std=c++11"]
                      )
            ]
)


# test.py
import example_wrapper
from time import time

array = [i for i in range(1000000)]
t0 = time()
result = example_wrapper.cython_wrap(array)
t1 = time()
print("Wrapped duration: {}".format(t1 - t0))

1 Ответ

0 голосов
/ 30 мая 2018

Очевидно, что отличие от накладных расходов на Cython, но почему оно так велико?

Вызов функции с оболочкой сложнее, чем кажется на первый взгляд:

def cython_wrap(array):
       return wrapped_function(array)

arrayсписок целых чисел, wrapped_function ожидает вектор чисел с плавающей запятой, поэтому цитон автоматически создает вектор и заполняет его значениями из списка.

wrapped_function возвращает вектор чисел с плавающей запятой, но по порядкучтобы быть использованным python, он должен быть преобразован в список python.Еще раз cython автоматически создает список python и заполняет его python-float, который довольно дорог в построении и соответствует float из возвращаемого вектора.

Как вы можете видеть, происходит большое копированиеи это объясняет накладные расходы, которые вы наблюдаете.

Здесь - это набор правил, которые cython автоматически применяет при преобразовании контейнеров из c ++ в python.

Еще одна проблема: выпередавая вектор array по значению, поэтому он должен быть скопирован.Ваше время написания кода на С ++ не включает в себя это копирование, и поэтому оно немного несправедливо.

Вы должны передать вектор по константной ссылке, т.е.

... wrapped_function(const std::vector<float> &array)

Еще одна вещь: выверните вектор, который также будет скопирован, и это время копирования снова не включено в ваши c ++ - тайминги.Однако все современные компиляторы применяют оптимизацию возвращаемого значения, поэтому здесь это не проблема.

...