Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/344.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
函数调用开销-为什么内置Python内置看起来比我的内置更快?_Python_C_Function Call_Overhead - Fatal编程技术网

函数调用开销-为什么内置Python内置看起来比我的内置更快?

函数调用开销-为什么内置Python内置看起来比我的内置更快?,python,c,function-call,overhead,Python,C,Function Call,Overhead,我对开销很感兴趣,所以我编写了一个最小的C扩展,导出了两个函数nop和starnop,它们几乎什么都不做。它们只是传递输入(两个相关函数位于顶部,其余只是乏味的锅炉铭牌代码): 模块c: #include <Python.h> static PyObject* aman_nop(PyObject *self, PyObject *args) { PyObject *obj; if (!PyArg_UnpackTuple(args, "arg", 1, 1, &ob

我对开销很感兴趣,所以我编写了一个最小的C扩展,导出了两个函数
nop
starnop
,它们几乎什么都不做。它们只是传递输入(两个相关函数位于顶部,其余只是乏味的锅炉铭牌代码):

模块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);
}
接下来,我将它们与纯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个块(每个块1000000个调用)的结果:

<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
0.099754±0.003917µs 0.103933±0.000585µs
0.097711±0.000661µs 0.094412±0.000612µs
0.065146±0.000728µs 0.064976±0.000605µs
0.039546±0.000671µs 0.039566±0.000452µs
0.056490±0.000873µs 0.056234±0.000181µs
0.060094±0.000799µs 0.059959±0.000170µs
0.090452±0.001077µs 0.098479±0.003314µs
0.086387±0.000817µs 0.086536±0.000714µs
现在我的实际问题是:尽管我的nop是用C编写的,什么都不做(
starnop
甚至不解析它的参数),但是内置的
hash
函数始终更快。我知道int在Python中是它们自己的散列值,所以
hash
在这里也是一个nop,但它并不比我的nop高,那么为什么速度会有差异呢

更新:完全忘记了:我在一台相当标准的x86_64机器上,linux gcc4.8.5。我使用
python3 setup.py install安装的扩展--user

Python函数调用中的大部分开销是创建
args
元组。参数解析还增加了一些开销

函数定义使用
METH_VARARGS
调用约定需要创建一个元组来存储所有参数。如果只需要一个参数,可以使用
METH\O
调用约定。使用
METH\u O
,不会创建元组。直接传递单个参数。我在您的示例中添加了一个
nop1
,它使用
METH\u O

可以使用
METH\u NOARGS
定义不需要参数的函数。请参阅
nop2
,了解尽可能少的开销

当使用
METH_VARARGS
时,可以通过直接解析
args
元组而不是调用
PyArg\u unpactuple
或相关的
PyArg
函数来略微降低开销。它稍微快一点。请参见
nop3

内置的
hash()
函数使用了
METH\O
调用约定

修改后的模块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_nop1(PyObject *self, PyObject *other)
{
  Py_INCREF(other);
  return other;
}

static PyObject* aman_nop2(PyObject *self)
{
  Py_RETURN_NONE;
}

static PyObject* aman_nop3(PyObject *self, PyObject *args)
{
  PyObject *obj;

  if (PyTuple_GET_SIZE(args) == 1) {
    obj = PyTuple_GET_ITEM(args, 0);
    Py_INCREF(obj);
    return obj;
  }
  else {
    PyErr_SetString(PyExc_TypeError, "nop3 requires 1 argument");
    return NULL;
  }
}

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.")},
  {"nop1",  (PyCFunction)aman_nop1, METH_O,
   PyDoc_STR("nop(arg) -> arg\n\nReturn arg unchanged.")},
  {"nop2",  (PyCFunction)aman_nop2, METH_NOARGS,
   PyDoc_STR("nop(arg) -> arg\n\nReturn arg unchanged.")},
  {"nop3",  (PyCFunction)aman_nop3, 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);
}
结果

$ python3 test.py  
<class 'int'>                                                0.080414 ± 0.004360 µs    0.086166 ± 0.003216 µs
<class 'bool'>                                               0.080501 ± 0.008929 µs    0.075601 ± 0.000598 µs
<method-wrapper '__add__' of int object at 0xa6dca0>         0.045652 ± 0.004229 µs    0.044146 ± 0.000114 µs
<built-in function hash>                                     0.035122 ± 0.003317 µs    0.033419 ± 0.000136 µs
<built-in function starnop>                                  0.044056 ± 0.001300 µs    0.044280 ± 0.001629 µs
<built-in function nop>                                      0.047297 ± 0.000777 µs    0.049536 ± 0.007577 µs
<built-in function nop1>                                     0.030402 ± 0.001423 µs    0.031249 ± 0.002352 µs
<built-in function nop3>                                     0.044673 ± 0.004041 µs    0.042936 ± 0.000177 µs
<function pnp at 0x7f946342d840>                             0.071846 ± 0.005377 µs    0.071085 ± 0.003314 µs
<function <lambda> at 0x7f946342d8c8>                        0.066621 ± 0.001499 µs    0.067163 ± 0.002962 µs
<built-in function nop2>                                     0.027736 ± 0.001487 µs    0.027035 ± 0.000397 µs
$python3 test.py
0.080414±0.004360µs 0.086166±0.003216µs
0.080501±0.008929µs 0.075601±0.000598µs
0.045652±0.004229µs 0.044146±0.000114µs
0.035122±0.003317µs 0.033419±0.000136µs
0.044056±0.001300µs 0.044280±0.001629µs
0.047297±0.000777µs 0.049536±0.007577µs
0.030402±0.001423µs 0.031249±0.002352µs
0.044673±0.004041µs 0.042936±0.000177µs
0.071846±0.005377µs 0.071085±0.003314µs
0.066621±0.001499µs 0.067163±0.002962µs
0.027736±0.001487µs 0.027035±0.000397µs

您是如何编译C代码的?你还没有告诉我们这条重要的信息。另外,Python是如何编译的?Python中是否启用了代码中没有的优化功能?@Sebivor只需调用安装脚本:
python3 setup.py install--user
。我总是假设使用Python本身编译时使用的相同编译器设置,除非您明确指定其他设置。我会更新这个问题。。。。我问的其他问题的答案是什么?你也有吗?如果不是,你没有做足够的研究来提出这个问题…你可以通过事先做研究来节省大量的打字时间,你知道吗?一个明智的开始是阅读你的编译器手册,它会告诉你很多在幕后发生的微妙优化,以及你以后可能会问到的其他有用的东西。@Sebivor relaxy,当我试图解释的时候,当很快被接受的答案证实编译器的问题在理论上是可能的,但不太可能解释。python构建系统和setuptools是非常复杂的,在这样一个简单的例子中,setuptools为您完成整个构建,使用与构建python相同的编译器设置—它究竟为什么要做任何不同的事情呢。我的台词是我唯一要做的事。哇,非常感谢!我会再等一会儿,但不知怎的,我怀疑会有更好的答案。再次感谢,这是一个完整的信息和愉快的答案。我的
nop
的速度也很好。
import numpy as np
from aman import nop, nop1, nop2, nop3, 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, nop1, nop3, 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)]))

# To test with no arguments
for globals['nop'] in (nop2,):
    print('{:60s}'.format(repr(globals['nop'])),
          mnsd([timeit('nop()', globals=globals) for i in range(10)]),
          '  ',
          mnsd([timeit('nop()',globals=globals) for i in range(10)]))
$ python3 test.py  
<class 'int'>                                                0.080414 ± 0.004360 µs    0.086166 ± 0.003216 µs
<class 'bool'>                                               0.080501 ± 0.008929 µs    0.075601 ± 0.000598 µs
<method-wrapper '__add__' of int object at 0xa6dca0>         0.045652 ± 0.004229 µs    0.044146 ± 0.000114 µs
<built-in function hash>                                     0.035122 ± 0.003317 µs    0.033419 ± 0.000136 µs
<built-in function starnop>                                  0.044056 ± 0.001300 µs    0.044280 ± 0.001629 µs
<built-in function nop>                                      0.047297 ± 0.000777 µs    0.049536 ± 0.007577 µs
<built-in function nop1>                                     0.030402 ± 0.001423 µs    0.031249 ± 0.002352 µs
<built-in function nop3>                                     0.044673 ± 0.004041 µs    0.042936 ± 0.000177 µs
<function pnp at 0x7f946342d840>                             0.071846 ± 0.005377 µs    0.071085 ± 0.003314 µs
<function <lambda> at 0x7f946342d8c8>                        0.066621 ± 0.001499 µs    0.067163 ± 0.002962 µs
<built-in function nop2>                                     0.027736 ± 0.001487 µs    0.027035 ± 0.000397 µs