【问题标题】:Cython : pure C loop optimizationCython:纯 C 循环优化
【发布时间】:2014-02-18 08:52:06
【问题描述】:

引用Cython documentation

Cython recognises the usual Python for-in-range integer loop pattern:
    for i in range(n):
        ...
If i is declared as a cdef integer type, it will optimise this into a pure C loop

我编写了两个版本的简单 Cython 函数,一个使用 Python range,另一个使用 for-from Pyrex 表示法(应该已弃用):

 def loop1(int start, int stop, int step):
    cdef int x, t = 0
    for x in range(start, stop, step):
        t += x
    return t

def loop2(int start, int stop, int step):
    cdef int x, t = 0
    for x from start <= x < stop by step:
        t += x
    return t

通过查看.cfile,我注意到两个循环的处理方式非常不同:

第一个实际上是使用 Python 对象创建 Python 范围。它附带了 50 行不必要的 Python-to-C C-to-Python 内容。

第二个已经优化成一个漂亮的纯C循环:

__pyx_t_1 = __pyx_v_stop;
__pyx_t_2 = __pyx_v_step;
for (__pyx_v_x = __pyx_v_start; __pyx_v_x < __pyx_t_1; __pyx_v_x+=__pyx_t_2) {

是我遗漏了什么还是我应该报告的错误?

【问题讨论】:

    标签: python loops optimization cython


    【解决方案1】:

    The docs 请提及:

    自动范围转换

    这将转换形式的语句 for i in range(...) to for i from ... 当 i 是任何 cdef 整数 类型,可以确定方向(即步骤的符号)

    我想 Cython 想在编译时知道步骤的符号,以便在 C 循环的结束条件下生成 &lt;&gt;

    另见Ticket #546 on Cython Trac

    【讨论】:

    • 我也尝试用unsigned int 替换int,但结果保持不变。而且我不知道如何告诉 Cython 步骤的标志。
    【解决方案2】:

    实际上,假设 start、stop 和 step 是 C 变量,则可以将 any for 范围内的循环转换为完全优化的 C 循环。你只需要写得巧妙一点。

    loop1()开头:

    def loop1(int start, int stop, int step):
        cdef int x, t = 0
        for x in range(start, stop, step):
            t += x
        return t
    

    Cython(当前)不知道如何优化它,因为它不知道 step 的符号。事实证明,这个问题的最简单的解决方案是解决一个稍微更普遍的问题。也就是说:

    def loop1(int start, int stop, int step):
        cdef:
            int x
            int count
            int t = 0
        for count, x in enumerate(range(start, stop, step)):
            t += x
        return t
    

    count 变量看起来没什么用,但不同版本的问题可能会在循环体中使用它。

    现在,手动计算索引:

    def loop1(int start, int stop, int step):
        cdef:
            int x
            int count
            int length
            int t = 0
        length = len(range(start, stop, step))  # could optimize this further
        for count in range(length):
            x = start + count*step
            t += x
        return t
    

    我已经尝试过了,我知道它会生成纯 C 代码(length = 行除外)。事实上,我已经在nogil 块中成功使用了它。 cython -a 显示循环本身的全白输出。

    这将创建两个额外的变量,以及一些死存储等,但我认为任何中等体面的编译器都应该能够消除-O2 上的那些变量。因此它适用于高性能循环。

    【讨论】:

    • 有道理,但是如果你知道步骤的符号,Pyrex 语法for x from start &lt;= x &lt; stop by step 仍然更方便。最好让 Cython 处理 range(a,b,c) 语法,就像讨论的 here 一样。
    • 最后的评论是 3 年前,推荐一个类似于我的解决方案。我不知道 Cython 开发人员现在在做什么,但不是那个错误。
    【解决方案3】:

    我知道这是一个非常古老的问题,但对于那些在谷歌上搜索并最终来到这里的人,我会发布一个答案。

    https://github.com/cython/cython/issues/3310#issuecomment-707252866

    for i in range(start, stop, step):
        ...
    

    到这里:

    i = start - step
    for _ in range(((stop - start) + (step - 1))//step):
        i += step
        ...    
    

    【讨论】:

      猜你喜欢
      • 2018-05-20
      • 2021-04-19
      • 2011-07-01
      • 1970-01-01
      • 2018-12-23
      • 1970-01-01
      • 1970-01-01
      • 2014-07-30
      • 2017-02-28
      相关资源
      最近更新 更多