簡體   English   中英

為什么在速度方面,cython循環的性能與python一相比降低了?

[英]why performance of cython loop has diminished in comparison with python one in terms of speed?

我正在嘗試通過使用cython功能來提高python代碼的速度。 我的python代碼由py_childpy_parent類以及py_backup函數組成,如下所示:

import random
from time import clock
import numpy as np
from libc.string cimport memcmp
## python code #################################################
class py_child:
    def __init__(self, move):
        self.move = move
        self.Q = 0
        self.N = 0

class py_parent:
    def __init__(self):
        self.children = []
    def add_children(self, moves):
        for move in moves:
            self.children.append(py_child(move))

def py_backup(parent, white_rave, black_rave):
    for point in white_rave:
        for ch in parent.children:
            if ch.move == point:
                ch.Q += 1
                ch.N += 1

    for point in black_rave:
        for ch in parent.children:
            if ch.move == point:
                ch.Q += 1
                ch.N += 1

這與cython的實現相同,通過使用memoryviews作為一些變量:

## cython ######################################################

cdef class cy_child:
    cdef public:
        int[:] move
        int Q
        int N
    def __init__(self, move):
        self.move = move
        self.Q = 0
        self.N = 0

cdef class cy_parent:
    cdef public:
        list children
        int[:, :] moves
    def __init__(self):
        self.children = []
    def add_children(self, moves):
        cdef int i = 0
        cdef int N = len(moves)
        for i in range(N):
            self.children.append(cy_child(moves[i]))

cpdef cy_backup(cy_parent parent_node, int[:, :] white_rave,int[:, :] black_rave):
    cdef int[:] move
    cdef cy_child ch
    for move in white_rave:
        for ch in parent_node.children:
            if memcmp(&move[0], &ch.move[0], move.nbytes) == 0:
                ch.Q += 1
                ch.N += 1

    for move in black_rave:
        for ch in parent_node.children:
            if memcmp(&move[0], &ch.move[0], move.nbytes) == 0:
                ch.Q += 1
                ch.N += 1

現在我想評估函數cy_backup,py_backup的代碼速度,因此我使用以下代碼:

### Setup variables #########################################
size = 11
board = np.random.randint(2, size=(size, size), dtype=np.int32)

for x in range(board.shape[0]):
    for y in range(board.shape[1]):
        if board[x,y] == 0:
            black_rave.append((x,y))
        else:
            white_rave.append((x,y))

py_temp = []
for i in range(size):
    for j in range(size):
        py_temp.append((i,j))

#### python arguments #######################################

py = py_parent()
py.add_children(py_temp)
# also py_temp, black_rave, white_rave

#### cython arguments #######################################
cy_temp = np.assarray(py_temp, , dtype= np.int32)
cy_black_rave = np.asarray(black_rave, dtype= np.int32)
cy_white_rave = np.asarray(white_rave, dtype= np.int32)
cy = cy_parent()
cy.add_children(cy_temp)

#### Speed test #################################################
%timeit py_backup(py_parent, black_rave, white_rave)
%timeit cy_backup(cy_parent, cy_black_rave, cy_white_rave)

當我運行程序時,我對結果感到驚訝:

1000 loops, best of 3: 759 µs per loop
100 loops, best of 3: 6.38 ms per loop

我期望cython比python快得多,特別是在使用memoryviews時。
為什么cython中的循環比python中的循環慢?
如果有人對加速cython中的代碼有任何建議,將不勝感激。
事先我為我的問題(包括太多代碼)道歉。

Cython內存視圖實際上僅針對訪問單個元素或片(通常在循環中)的一件事進行了優化。

# e.g.
cdef int i
cdef int[:] mview = # something
for i in range(mview.shape[0]):
   mview[i] # do some work with this....

這種類型的代碼可以直接轉換為高效的C代碼。 對於幾乎所有其他操作,將memoryview視為Python對象。

不幸的是,幾乎所有代碼都沒有利用memoryview擅長的一件事,因此您無法獲得真正的加速。 相反,實際上情況更糟,因為您已經添加了額外的一層,並且小長度2個memoryview的整個負載將非常糟糕。

我的建議實際上只是使用列表-它們實際上對這種事情非常有用,而且我還不清楚如何重寫您的代碼以真正使用Cython加快速度。


我發現了一些小的優化:通過查看cython -a生成的突出顯示的html文件,您可以很好地了解如何優化Cython。 您會看到memoryview的一般迭代很慢(即純Python)。 通過改變你會得到改善

# instead of:
# for move in white_rave:
for i in range(white_rave.shape[0]):
    move = white_rave[i,:]

這使Cython可以有效地迭代memoryview。

通過關閉memcmp行的一些安全檢查,可以提高速度:

with cython.boundscheck(False), cython.initializedcheck(False):
   if memcmp(&move[0], &ch.move[0], move.nbytes) == 0:

(您需要cimport cython )。 如果執行此操作,但尚未初始化ch.move或兩個memoryviews沒有至少一個元素,則您的程序可能會崩潰。


我意識到這不是一個有用的答案,但是只要您希望將child保留為Python類(事件是cdef ),就真的沒有什么可以加快它的方法了。 您可能會考慮將其更改為C結構(可以具有C數組),但是隨后您失去了使用Python的所有好處(即,您必須管理自己的內存,並且無法從Python代碼輕松訪問它) )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM