Python:嵌套for循环非常慢 - 读取RLE压缩的3D数据

时间:2017-06-26 15:43:23

标签: python performance

我需要将使用游程编码(RLE)压缩的3D数据读入python中的3D numpy数组。在Matlab中,使用嵌套循环大约需要一秒钟。但是在python中需要48秒!

这是我的代码:

# Preallocate 3D voxel grid
vox_size = [200,150,200];
voxelGrid3D = np.zeros([200,150,200], dtype=np.uint32);

# Get values from RLE encoded 3D scene: 
# Example:
# 0, (3), 4, (2) --> corresponds to --> 00044 
# --> value == [0, 4]
# --> value_reps == [3, 2]
value = labelsRleCompressed[::2];
value_reps = labelsRleCompressed[1::2];

vox_idx = 0;
vox_idx_all = 0;
num_elements = value_reps.size; # Number of elements to convert
for m in np.arange(0,num_elements):
    numReps = value_reps[m];
    currentValue = value[m];
    for l in np.arange(0,numReps):
    # Compute respective grid indices
        i = (np.floor(vox_idx_all / (vox_size[0] * vox_size[1]) ) % vox_size[2]);
        j = (np.floor(vox_idx_all / (vox_size[0]) ) % vox_size[1]);
        k = (np.floor(vox_idx_all ) % vox_size[0]);

    # Fill grid with label value
        voxelGrid3D[i,j,k] = currentValue;
        vox_idx_all = vox_idx_all + 1;

即使我删除内部循环并将其替换为预先计算的网格索引+重塑函数,整个过程仍需要10秒钟!

voxelGrid = np.zeros(num_voxels,dtype=np.uint32)
repIter = 0;
numReps = 0;
vox_idx = 0;
for counter in np.arange(0,num_voxels):
    if repIter == numReps:
        numReps = value_iter[vox_idx];
        currentValue = value[vox_idx];
        vox_idx = vox_idx + 1;
        voxelGrid[counter] = currentValue
        repIter = 1;
    else:
        voxelGrid[counter] = currentValue
        repIter = repIter + 1;
voxelGrid3D = np.reshape(voxelGrid,(vox_size[0],vox_size[1],vox_size[2]))

这对我的申请来说太慢了。有谁知道如何让这更快?

1 个答案:

答案 0 :(得分:0)

加速循环

首先不要使用np.arange来创建迭代器(这将创建一个迭代的数组)。请改用范围(Python3)或xrange(Python2)。这应该会使性能提高几个百分点,但这不是你真正的瓶颈。

Matlab有一个及时编译器来执行相对良好的循环,CPython默认没有这个。但是有一个及时的编译器叫做numba http://numba.pydata.org/。在文档中,您将找到可以编译为本机机器代码的受支持函数。当使用numba时,我还建议用循环而不是矢量化代码来编写东西,因为这对编译器来说更容易处理。

我稍微修改了你的代码。

def decompress_RLE(labelsRleCompressed,vox_size):
    res=np.empty(vox_size[0]*vox_size[1]*vox_size[2],np.uint32)

    ii=0
    for i in range(0,labelsRleCompressed.size,2):
        value=labelsRleCompressed[i]
        rep=labelsRleCompressed[i+1]
        for j in range(0,rep):
            res[ii]=value
            ii=ii+1

    res=res.reshape((vox_size[0],vox_size[1],vox_size[2]))

    return res

创建基准化数据

vox_size=np.array((300,300,300),dtype=int32)
#create some data
labelsRleCompressed=np.random.randint(0, 500, 
size=vox_size[0]*vox_size[1]*vox_size[2]/2, dtype=np.uint32)
labelsRleCompressed[1::2]=4

简单地使用生成的数据调用函数会导致 7.5 秒的运行时间,这是一个相当差的性能。

现在让我们使用numba。

import numba
nb_decompress_RLE = numba.jit("uint32[:,:,:](uint32[:],int32[:])",nopython=True)(decompress_RLE) #stick to the datatypes written in the decorator

使用测试数据调用已编译的nb_decompress_RLE会导致 0.0617 秒的运行时间。一个很好的加速119倍!使用np.copy简单复制数组只需要快3倍。