【发布时间】:2018-05-23 21:14:42
【问题描述】:
我一直对开销感兴趣,所以我编写了一个最小的 C 扩展,导出两个函数 nop 和 starnop 或多或少什么都不做。他们只是通过他们的输入(两个相关的函数就在顶部,其余的只是乏味的样板代码):
amanmodule.c:
#include <Python.h>
static PyObject* aman_nop(PyObject *self, PyObject *args)
{
PyObject *obj;
if (!PyArg_UnpackTuple(args, "arg", 1, 1, &obj))
return NULL;
Py_INCREF(obj);
return obj;
}
static PyObject* aman_starnop(PyObject *self, PyObject *args)
{
Py_INCREF(args);
return args;
}
static PyMethodDef AmanMethods[] = {
{"nop", (PyCFunction)aman_nop, METH_VARARGS,
PyDoc_STR("nop(arg) -> arg\n\nReturn arg unchanged.")},
{"starnop", (PyCFunction)aman_starnop, METH_VARARGS,
PyDoc_STR("starnop(*args) -> args\n\nReturn tuple of args unchanged")},
{NULL, NULL}
};
static struct PyModuleDef amanmodule = {
PyModuleDef_HEAD_INIT,
"aman",
"aman - a module about nothing.\n\n"
"Provides functions 'nop' and 'starnop' which do nothing:\n"
"nop(arg) -> arg; starnop(*args) -> args\n",
-1,
AmanMethods
};
PyMODINIT_FUNC
PyInit_aman(void)
{
return PyModule_Create(&amanmodule);
}
setup.py:
from setuptools import setup, extension
setup(name='aman', version='1.0',
ext_modules=[extension.Extension('aman', ['amanmodule.c'])],
author='n.n.',
description="""aman - a module about nothing
Provides functions 'nop' and 'starnop' which do nothing:
nop(arg) -> arg; starnop(*args) -> args
""",
license='public domain',
keywords='nop pass-through identity')
接下来,我将它们与纯 Python 实现和几个几乎什么都不做的内置函数进行比较:
import numpy as np
from aman import nop, starnop
from timeit import timeit
def mnsd(x): return '{:8.6f} \u00b1 {:8.6f} \u00b5s'.format(np.mean(x), np.std(x))
def pnp(x): x
globals={}
for globals['nop'] in (int, bool, (0).__add__, hash, starnop, nop, pnp, lambda x: x):
print('{:60s}'.format(repr(globals['nop'])),
mnsd([timeit('nop(1)', globals=globals) for i in range(10)]),
' ',
mnsd([timeit('nop(True)',globals=globals) for i in range(10)]))
第一个问题我没有在方法方面做一些迟钝的事情?
10 个区块的结果,每个区块有 1,000,000 次调用:
<class 'int'> 0.099754 ± 0.003917 µs 0.103933 ± 0.000585 µs
<class 'bool'> 0.097711 ± 0.000661 µs 0.094412 ± 0.000612 µs
<method-wrapper '__add__' of int object at 0x8c7000> 0.065146 ± 0.000728 µs 0.064976 ± 0.000605 µs
<built-in function hash> 0.039546 ± 0.000671 µs 0.039566 ± 0.000452 µs
<built-in function starnop> 0.056490 ± 0.000873 µs 0.056234 ± 0.000181 µs
<built-in function nop> 0.060094 ± 0.000799 µs 0.059959 ± 0.000170 µs
<function pnp at 0x7fa31c0512f0> 0.090452 ± 0.001077 µs 0.098479 ± 0.003314 µs
<function <lambda> at 0x7fa31c051378> 0.086387 ± 0.000817 µs 0.086536 ± 0.000714 µs
现在我的实际问题是:即使我的 nops 是用 C 编写的并且什么都不做(starnop 甚至不解析它的参数),内置的 hash 函数始终更快。我知道 int 在 Python 中是它们自己的哈希值,所以 hash 在这里也是一个 nop 但它并不比我的 nop 更糟糕,那么为什么速度差异?
更新:完全忘记了:我在一个非常标准的 x86_64 机器上,linux gcc4.8.5。我使用 python3 setup.py install --user 安装的扩展。
【问题讨论】:
-
你是如何编译你的 C 代码的?你还没有告诉我们这条重要的信息。另外,Python是如何编译的?是否在 Python 中启用了您的代码中没有的优化?
-
@Sebivor 只是调用设置脚本:
python3 setup.py install --user。我总是假设使用相同的编译器设置 Python 本身被编译,除非你明确指定。我会更新问题。 -
...以及我问的其他问题的答案?你也有这些吗?如果没有,你没有做足够的研究来问这个问题......
-
您可以通过事先进行研究来节省大量打字(关于这个问题),你知道吗?一个明智的起点是阅读您的编译器手册页,它会告诉您所有在幕后发生的细微优化,以及您以后可能会问到的其他有用的东西。
-
@Sebivor 放松,因为我试图解释并且很快被接受的答案证实编译器问题在理论上是可能的但不太可能的解释。 python 构建系统和 setuptools 非常复杂,在像这样的简单情况下,setuptools 使用与构建 python 相同的编译器设置为您完成整个构建 - 为什么它应该做任何不同的事情。我展示的那条线实际上是我唯一需要做的事情。
标签: python c function-call overhead