C扩展性能测试

  1. 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);
}
  1. 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')
  1. main.py
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)]))

<class ‘int’> 0.060841 ± 0.000619 µs 0.062638 ± 0.000458 µs
<class ‘bool’> 0.052788 ± 0.000301 µs 0.053092 ± 0.000665 µs
<method-wrapper ‘add’ of int object at 0x559de3389400> 0.047419 ± 0.000263 µs 0.047689 ± 0.000519 µs
0.021551 ± 0.000149 µs 0.021716 ± 0.000081 µs
0.034019 ± 0.000578 µs 0.033485 ± 0.000249 µs
0.041586 ± 0.000226 µs 0.041690 ± 0.000237 µs
<function pnp at 0x7fe098bd6268> 0.044545 ± 0.000525 µs 0.043549 ± 0.000268 µs
<function at 0x7fe098bd62f0> 0.043362 ± 0.000308 µs 0.042609 ± 0.000315 µs

Python函数调用中的大部分(大部分?)开销是创建 args元组。参数解析还增加了一些开销。
使用METH_VARARGS调用约定定义的函数需要创建一个元组来存储所有参数。如果只需要一个参数,则可以使用METH_O调用约定。使用METH_O,不创建任何元组。单个参数直接传递。我在nop1您的示例中添加了一个METH_O。可以使用定义不需要参数的函数METH_NOARGS。看到nop2以获取最少的开销。使用时METH_VARARGS,可以通过直接解析args元组而不是调用PyArg_UnpackTuple或相关PyArg_函数来稍微减少开销。它稍微快一点。看到nop3。内置hash()函数使用了METH_O调用约定。

  1. amanmodule1.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);
}
  1. main.py
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
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
<function pnp at 0x7f946342d840> 0.071846 ± 0.005377 µs 0.071085 ± 0.003314 µs
<function at 0x7f946342d8c8> 0.066621 ± 0.001499 µs 0.067163 ± 0.002962 µs
0.027736 ± 0.001487 µs 0.027035 ± 0.000397 µs

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值