【问题标题】:How to reduce Cython compiled code's interaction with Python如何减少 Cython 编译代码与 Python 的交互
【发布时间】:2020-12-29 23:43:23
【问题描述】:

我试图通过使用 Cython 编译为 C 来减少计算量特别大的 Python 代码。令人惊讶的是,我在加速它方面几乎没有成功。使用原始 Python 模块,代码运行时间约为 45 秒,使用 Cython 编译模块运行时间约为 45 秒。

使用 annotate=True 编译时,我留下了一大堆黄色(ish)行,表明仍然存在大量 Python 交互。

我已通过关闭boundscheck (False)cdivision (True) 对此作出回应。这没有效果。下面的 sn-p 代码是从这个模块中提取的,它坚持与 Python 交互。为什么是这样?这里不需要与任何预先存在的 Python 模块交互,都是非常简单的算术运算?

cpdef float __distance_between(point1, point2)  except? -2:
    return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** (1/2)

point1point2 是 Python 列表,每个列表都包含 2 个 doubles 例如:[611811.997, -871083.372] Cython 为 sn-p 中的 return 行生成的 C 代码为如下:

+05: cpdef float __distance_between(point1, point2)  except? -2:
+06:     return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2)**(1/2)
  __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_point1, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_point2, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_3 = PyNumber_Subtract(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = PyNumber_Power(__pyx_t_3, __pyx_int_2, Py_None); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_point1, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_point2, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_4 = PyNumber_Subtract(__pyx_t_3, __pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyNumber_Power(__pyx_t_4, __pyx_int_2, Py_None); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __pyx_t_4 = PyNumber_Add(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __Pyx_PyInt_From_long((1 / 2)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = PyNumber_Power(__pyx_t_4, __pyx_t_1, Py_None); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_5 = __pyx_PyFloat_AsFloat(__pyx_t_2); if (unlikely((__pyx_t_5 == (float)-1) && PyErr_Occurred())) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_r = __pyx_t_5;
  goto __pyx_L0;

如何更好地优化代码或编译以使这个 sn-p 独立于 Python?

此模块中还有许多其他行都呈现为黄色,但从这个问题开始可能会让我更好地了解如何解决其余问题。

【问题讨论】:

    标签: python cython


    【解决方案1】:

    您还没有cdefed 任何变量; 知道他们是float,Cython 不知道。告诉它。

    cpdef float __distance_between((double, double)point1, (double, double)point2)  except? -2:
        return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** (1/2)
    

    【讨论】:

    • 旁注:确保使用-3 编译它以选择使用Python 3 语法,否则1/2 将被解释为0;或者,只需将其更改为文字 0.5(或 1. / 2. 等),无论您使用哪种语法(Py2 与 Py3),它都会起作用。
    • 是的,这就是问题所在。并感谢 1/2 上的指针。
    • 但是,如果这些点是真正的 python 浮点列表,则没有 Cython 数据类型可以有效地表达这一点。您刚刚将工作转移到函数调用中的一些转换。
    • @DavidW:它仍然会显着提高性能。当然,您需要预先进行转换,但它会将一堆 PyNumber 通用 API 调用转换为原始 C 操作。四个解包和一个重新打包不是很好,但在这之间都是原始的 C double 操作,比六个通用的 PyNumber 操作要好得多(每个操作都必须解包两个操作数,执行数学并将结果重新打包到 Python float).
    • @ShadowRanger 是的,公平点 - 我认为你是对的,它会有所帮助,而且我认为在不更确切地了解 OP 如何使用该功能的情况下进行优化是可能的。
    猜你喜欢
    • 1970-01-01
    • 2012-01-16
    • 2017-01-16
    • 2014-08-22
    • 1970-01-01
    • 2016-11-27
    • 1970-01-01
    • 2011-10-22
    • 1970-01-01
    相关资源
    最近更新 更多