用 cython 包装的 C++ 函数的计时
Timing of a C++ function wrapped with cython
我真的不知道怎么问这个问题,但我会尽量说清楚。
我正在计时来自 python 的 C++ 函数调用。 C++ 函数用 cython 包装。
我目前正在计时 cython 函数的 python 调用,我得到 time.time()
大约 52.9 毫秒。另一方面,我用 C++ std::chrono::high_resolution_clock
库对整个 C++ 函数进行计时。
事实是,我在 C++ 中测量了 17.1 毫秒。
C++ 函数是这样声明的 vector<float> cppfunc(vector<float> array, int a, int b, int c);
并且是一个 class 方法。
cython代码只调用了C++的class方法。该向量包含大约 320k 个元素。
我想知道这两个测量时间是否可以这样比较?
如果可以,什么可以解释这种差距?
如果没有,我应该使用哪个计时工具?
Edit1:(评论中的链接)两个时序库对于我的用例都足够精确(我的 arch 上的 cpp 为 10e-9,[=36= 为 10e-6 ]).
Edit2: 添加了简化代码来说明我的观点。使用此代码,python 调用持续时间(~210 毫秒)是实习生 cpp 持续时间(~28 毫秒)的 8 倍。
// 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))
很明显是cython开销的区别,但是为什么这么大呢?
包装函数的调用比表面上看起来更复杂:
def cython_wrap(array):
return wrapped_function(array)
array
是一个整数列表,wrapped_function
需要浮点向量,因此 cython 会自动创建一个向量并用列表中的值填充它。
wrapped_function
return 是一个浮点向量,但为了被 python 使用,必须将其转换为 python 列表。 cython 再次自动创建一个 python-list 并用 python-floats 填充它,构建起来非常昂贵并且对应于 returned 向量中的浮点数。
如您所见,正在进行大量复制,这解释了您观察到的开销。
Here 是 cython 在从 c++ 容器转换为 python.
时自动应用的规则集
另一个问题:您正在按值传递向量 array
,因此必须复制它。您的 c++ 代码时间不包括此复制,因此有点不公平。
您应该通过常量引用传递向量,即
... wrapped_function(const std::vector<float> &array)
还有一件事:您 return 一个可能的向量也将被复制,并且此复制时间再次不包括在您的 c++ 计时中。然而,所有现代编译器都应用 return 值优化,所以这不是问题。
我真的不知道怎么问这个问题,但我会尽量说清楚。
我正在计时来自 python 的 C++ 函数调用。 C++ 函数用 cython 包装。
我目前正在计时 cython 函数的 python 调用,我得到 time.time()
大约 52.9 毫秒。另一方面,我用 C++ std::chrono::high_resolution_clock
库对整个 C++ 函数进行计时。
事实是,我在 C++ 中测量了 17.1 毫秒。
C++ 函数是这样声明的 vector<float> cppfunc(vector<float> array, int a, int b, int c);
并且是一个 class 方法。
cython代码只调用了C++的class方法。该向量包含大约 320k 个元素。
我想知道这两个测量时间是否可以这样比较? 如果可以,什么可以解释这种差距? 如果没有,我应该使用哪个计时工具?
Edit1:(评论中的链接)两个时序库对于我的用例都足够精确(我的 arch 上的 cpp 为 10e-9,[=36= 为 10e-6 ]).
Edit2: 添加了简化代码来说明我的观点。使用此代码,python 调用持续时间(~210 毫秒)是实习生 cpp 持续时间(~28 毫秒)的 8 倍。
// 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))
很明显是cython开销的区别,但是为什么这么大呢?
包装函数的调用比表面上看起来更复杂:
def cython_wrap(array):
return wrapped_function(array)
array
是一个整数列表,wrapped_function
需要浮点向量,因此 cython 会自动创建一个向量并用列表中的值填充它。
wrapped_function
return 是一个浮点向量,但为了被 python 使用,必须将其转换为 python 列表。 cython 再次自动创建一个 python-list 并用 python-floats 填充它,构建起来非常昂贵并且对应于 returned 向量中的浮点数。
如您所见,正在进行大量复制,这解释了您观察到的开销。
Here 是 cython 在从 c++ 容器转换为 python.
时自动应用的规则集另一个问题:您正在按值传递向量 array
,因此必须复制它。您的 c++ 代码时间不包括此复制,因此有点不公平。
您应该通过常量引用传递向量,即
... wrapped_function(const std::vector<float> &array)
还有一件事:您 return 一个可能的向量也将被复制,并且此复制时间再次不包括在您的 c++ 计时中。然而,所有现代编译器都应用 return 值优化,所以这不是问题。