关于python 3.x:用cython包装的C ++函数的时序

Timing of a C++ function wrapped with cython

我真的不知道如何提出这个问题,但我会尽量保持清醒。

我正在计算来自python的C ++函数调用。 C ++函数包含在cython中。
我目前正在计时cython函数的python调用,我用time.time()得到52.9 ms。另一方面,我使用C ++ std::chrono::high_resolution_clock库为整个C ++函数计时。

问题是,我在C ++中测量的是17.1 ms。

C ++函数声明为此vector cppfunc(vector array, int a, int b, int c);并且是A类方法。

cython代码只调用C ++类方法。该向量包含大约320k个元素。

我想知道这两个测量时间是否可以这样比较?
如果可以,有什么可以解释这个差距?
如果没有,我应该使用哪种计时工具?

Edit1 :(注释中的链接)两个时序库对于我的用例来说足够精确(我的拱门上的cpp为10e-9,python为10e-6)。

Edit2:添加简化代码来说明我的观点。使用此代码,python调用持续时间(~210ms)是实习生cpp持续时间(~28ms)的8倍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 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
"
, 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))


显然不同之处在于cython开销,但为什么它如此之大?

包裹函数的调用比眼睛更复杂:

1
2
def cython_wrap(array):
       return wrapped_function(array)

array是一个整数列表,wrapped_function期望浮点数的向量,因此cython会自动创建一个向量并使用列表中的值填充它。

wrapped_function返回浮点数向量,但为了被python使用,它必须转换为python-list。再次,cython会自动创建一个python-list,并使用python-floats填充它,这些构建起来非常昂贵并且对应于返回向量中的浮点数。

正如您所看到的,正在进行大量复制,这解释了您正在观察的开销。

以下是从c ++ - containers转换为python时cython自动应用的一组规则。

另一个问题:您通过值传递向量array,因此必须复制它。你的c ++代码的时间不包括这种复制,因此它有点不公平。

你应该通过const-reference传递向量,即

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

还有一件事:你返回一个可能被复制的向量,这个复制时间再次不包含在你的c ++ - 时序中。但是,所有现代编译器都应用返回值优化,因此这不是问题。