【问题标题】:Cython Basics: How to speed up common c functions like random and math functions?Cython 基础知识:如何加速常见的 c 函数,如随机函数和数学函数?
【发布时间】:2019-07-14 20:34:05
【问题描述】:

我正在努力学习 Cython(请参阅 How to Cythonize a Python Class with an attribute that is a function?

我有以下想要加速的功能,但 Cython 说两条线都通过 python:

cdef selectindex(float fitnesspref):
    r = np.random.rand()
    return int(log(r) / log(fitnesspref))

所以我需要得到一个随机数(我最初只是使用 Python 调用中的 random(),但切换到 Numpy 希望它更快或者 Cythonize 更好。然后我需要记录两个浮点数,将它们分开,然后将结果作为整数返回。没什么太难的,应该是一个很好的问题,人们可以稍后使用。我正在努力通过Cython docs 找到我需要的所有信息的简单解决方案或谷歌。我本以为每个教程都是这样,但我运气不好。

但环顾四周,我找不到一个很好的简单解决方案。例如:Canonical way to generate random numbers in Cython

有没有最好但更简单的方法来 Cythonize 这样的函数?

顺便说一句,abs() 也有类似的问题,但在我将所有内容更改为 cdefs 后,它似乎自动切换到使用 C 版本。

这是 Cython 生成的 HTML 的结果:

我经常碰到的另一个常见问题是:

start = time.time()

即有没有一种简单的方法可以使用 C 代码来加快开始和结束时间?我在内部循环中有那条线,所以它减慢了速度。但这对我想做的事情真的很重要。

更新 1: 我正在尝试遵循 cmets 中的建议,但它们似乎并没有像我预期的那样发挥作用(这就是为什么这一切首先让我感到困惑。)例如,这是我的随机的 c 版本编写并编译。为什么还是黄色?

更新 2: 好的,我进一步研究了它,这就是为什么它不会一直编译到 C:

它正在检查以确保我没有被零除,即使它是一个常数并且永远不可能为零。你如何摆脱它?

【问题讨论】:

  • 谢谢,Sachin,我不知道你能做到。从现在开始我会使用它。
  • 在大多数情况下,您只想在 C 标准库中找到等效的函数并使用它。您链接到的关于随机数的问题稍微复杂一些,因为他们希望在多个线程中安全地生成数字,但如果您不关心这个问题,那么也有一个简单的 C 标准库函数
  • 附带说明,您可能还应该在 selectindex 上设置返回类型(即改用 cdef int selectindex(float fitnesspref):
  • 一旦你发现 C 函数生成随机数,别忘了给 r 添加一个类型!
  • @BruceNielson 关于零除:1)这可能不是问题——C 编译器会看到它是一个常量并跳过永远不会发生的分支——所以不要太担心黄色; 2) cdivision compiler argument 将关闭它。与以往一样,您应该尽可能在本地应用编译器参数(即函数装饰器或 with 语句),但值得仔细查看它们以查看可以禁用的内容

标签: python python-3.x cython


【解决方案1】:

对于任何关注我的人,以下是最终答案:

许多常用函数,包括 len() 已经内置。如果您将其切换为使用数组,它会自动编译为 C。See this link.

对于其余部分,需要以下导入:

from libc.stdlib cimport rand, RAND_MAX
from libc.math cimport log

替换对 random.random() 的调用:

@cython.cdivision(True)
cdef float crandom() except -1: 
    return  <float>rand() / <float>RAND_MAX

替换对 random.randint() 的调用:

@cython.cdivision(True)
cdef int crandint(int lower, int upper) except -1:
    return (rand() % (upper - lower + 1)) + lower

我的 selectindex 函数被重写为:

cdef int selectindex(float fitnesspref, int popsize) except -1:
    cdef float r
    cdef int val
    r = crandom() + 0.00000000001
    return <int>(log(r) / log(fitnesspref))

0.00000000001 是必要的,因为 C 和 Python 在这里的行为略有不同。 Python 版本显然不会从随机调用中返回一个直接向上的零,但随机调用经常这样做。零日志是未定义的。这可能是因为我的 c 版本仅使用有限数量的 int 作为起点。

我从来没有想出替换 time.time() 的方法。

我希望这可以帮助一些追随我的新手。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-07
    • 2013-06-27
    • 2021-03-01
    • 1970-01-01
    • 2015-12-20
    • 1970-01-01
    相关资源
    最近更新 更多