【问题标题】:Improving Python + numpy array allocation/initialization performance提高 Python + numpy 数组分配/初始化性能
【发布时间】:2014-07-03 14:01:45
【问题描述】:

我正在编写一个 python 程序,使用 DLL 的一些外部功能。 我的问题是将矩阵(python 中的 numpy 数组)传入和传出 C 代码,现在我使用以下代码从 DLL 接收数据:

peak_count = ct.c_int16()
peak_wl_array = np.zeros(512, dtype=np.double)
peak_pwr_array = np.zeros(512, dtype=np.double)

res = __dll.DLL_Search_Peaks(ctypes.c_int(data.shape[0])
                             ctypes.c_void_p(data_array.ctypes.data),
                             ctypes.c_void_p(peak_wl_array.ctypes.data),
                             ctypes.c_void_p(peak_pwr_array.ctypes.data),
                             ct.byref(peak_count))

它就像一个魅力,但我的问题是 numpy 分配速度 - 即使没有调用 DLL(刚刚评论)我有 每 100`000 次调用 3.1 秒。

它只是用 np.zeros() 分配并用 ctypes.c_void_p(D.ctypes.data) 获取可写指针

我需要每秒处理大约 20`000 个调用,所以几乎所有时间都花在分配内存上。

我考虑过 cython,但它不会加速 numpy 数组,所以我不会获得任何利润。

是否有更快的方法从 C 编写的 DLL 接收类似矩阵的数据。

【问题讨论】:

    标签: python c performance numpy


    【解决方案1】:

    内存操作很昂贵,numpy 或其他。

    如果要分配大量数组,最好看看是否可以只分配一次,然后使用视图或子数组来仅使用数组的一部分:

    import numpy as np
    
    niters=10000
    asize=512
    
    def forig():
        for i in xrange(niters):
            peak_wl_array = np.empty((asize), dtype=np.double)
            peak_pwr_array = np.empty((asize), dtype=np.double)
    
        return peak_pwr_array
    
    
    def fviews():
        peak_wl_arrays  = np.empty((asize*niters), dtype=np.double)
        peak_pwr_arrays = np.empty((asize*niters), dtype=np.double)
    
        for i in xrange(niters):
            # create views
            peak_wl_array  = peak_wl_arrays[i*asize:(i+1)*asize]
            peak_pwr_array = peak_pwr_arrays[i*asize:(i+1)*asize]
            # then do something
    
        return peak_pwr_emptys
    
    
    def fsubemptys():
        peak_wl_arrays  = np.empty((niters,asize), dtype=np.double)
        peak_pwr_arrays = np.empty((niters,asize), dtype=np.double)
    
        for i in xrange(niters):
            # do something with peak_wl_arrays[i,:]
    
        return peak_pwr_emptys
    
    
    import timeit
    
    print timeit.timeit(forig,number=100)
    print timeit.timeit(fviews,number=100)
    print timeit.timeit(fsubemptys,number=100)
    

    跑步给予

    3.41996979713
    0.844147920609
    0.00169682502747
    

    请注意,另一方面,如果您正在使用(例如)np.zeros,那么您大部分时间都在初始化内存,而不是分配内存,而且这总是需要更长的时间,擦除大部分这些方法之间的区别:

    4.20200014114
    5.43090081215
    4.58127593994
    

    在较新的系统上到主内存的良好单线程带宽将大约为 ~10GB/s(10 亿双/秒),所以它总是需要大约

    1024 双打/调用/(10 亿双打/秒)~ 1 微秒/调用

    将内存清零,这已经是您所看到的大部分时间。不过,如果您在调用之前初始化一个大型数组,总执行时间将相同,但每次调用的延迟会更低。

    【讨论】:

    • 是分配昂贵,还是初始化?我认为是第二个,这意味着np.empty 比np.zeros 快(很多)。
    • 注意:我的评论是指您的第一段。最后一段回答了它,但我认为这更适合您的回答开头;这可能是这里的主要问题。
    • 对不起,@Evert,你说得对,我只是添加了那部分......你认为现在我添加了那部分更有意义,还是我仍然应该重新 -根据您的建议考虑吗?
    • 由于处理细节,我无法一次分配所有内存。请注意,最“昂贵”的操作不是数组分配,而是检索可以通过调用 ctypes.c_void_p(data_array.ctypes.data) 传递给 DLL 的指针
    • @Makc 你能用显示哪个时间细分的数据更新你的问题吗?我买了你有一个瓶颈,即使那部分被注释掉了。
    猜你喜欢
    • 2018-01-08
    • 1970-01-01
    • 2011-01-12
    • 2018-12-04
    • 2011-05-30
    • 1970-01-01
    • 2015-08-05
    • 1970-01-01
    • 2014-06-28
    相关资源
    最近更新 更多