【问题标题】:How order of numpy array influence on multiplication speed?numpy数组的顺序如何影响乘法速度?
【发布时间】:2014-07-23 20:24:24
【问题描述】:

numpy 数组的顺序如何影响乘法速度?以及如何根据矩阵的大小自动选择它?

问题最初来自使用 cudamat 的代码:

def test_mat():
    #need to init cublas?
    # cm.cublas_init()

    n = 1024

    for i in xrange(1,20):  # 2^15 max or python fails
        m= 2
        m=m**i
        # print m
        print i
        try:
            t0= time.time()
            # cpum1 = np.array(np.random.rand(n, m)*10, dtype=np.float32, order='C')
            # cpum2 = np.array(np.random.rand(m, 1)*10, dtype=np.float32, order='C')
            #CUDA need fortran order of array for speed?
            cpum1 = np.array(np.random.rand(n, m)*10, dtype=np.float32, order='F')
            cpum2 = np.array(np.random.rand(m, 1)*10, dtype=np.float32, order='F')
            c = np.dot(cpum2.T, cpum1.T)
            print (time.time()-t0)

            t0= time.time()
            gpum1 = cm.CUDAMatrix(cpum1)
            gpum2 = cm.CUDAMatrix(cpum2)
            gm = cm.dot(gpum2.T, gpum1.T)
            gm.copy_to_host()
            print (time.time()-t0)
        except:
            pass

    # cm.cublas_shutdown()

    print 'done' 

这是我做过的一些测试,但我需要一些理论观点。

def test_order(m,n):            
    #default
    a = np.array(np.random.rand(m, n)*10, dtype=np.float32)
    b = np.array(np.random.rand(n, m)*10, dtype=np.float32)

    t0= time.time()
    c = np.dot(a,b)
    print (time.time()-t0)

    #1
    a = np.array(np.random.rand(m, n)*10, dtype=np.float32, order='C')
    b = np.array(np.random.rand(n, m)*10, dtype=np.float32, order='C')

    t0= time.time()
    c = np.dot(a,b)
    print (time.time()-t0)

    #2
    a = np.array(np.random.rand(m, n)*10, dtype=np.float32, order='C')
    b = np.array(np.random.rand(n, m)*10, dtype=np.float32, order='F')

    t0= time.time()
    c = np.dot(a,b)
    print (time.time()-t0)

    #3
    a = np.array(np.random.rand(m, n)*10, dtype=np.float32, order='F')
    b = np.array(np.random.rand(n, m)*10, dtype=np.float32, order='C')

    t0= time.time()
    c = np.dot(a,b)
    print (time.time()-t0)

    #4
    a = np.array(np.random.rand(m, n)*10, dtype=np.float32, order='F')
    b = np.array(np.random.rand(n, m)*10, dtype=np.float32, order='F')

    t0= time.time()
    c = np.dot(a,b)
    print (time.time()-t0)


    print 'done'    

m= 1024*10
n= 1024*1
7.125
7.14100003242
6.95299983025
8.14100003242
7.15600013733

m= 1024*1
n= 1024*10  
0.718999862671
0.734000205994
0.641000032425
0.656000137329
0.655999898911

这是测试内存使用峰值的代码:

import numpy as np
import time
from memory_profiler import profile

@profile    
def test_order_():

    m= 1024*1
    n= 1024*10

    #what used by default when c= np.dot(a,b)
    c = np.array(np.zeros((m, m)), dtype=np.float32, order='C')
    #c = np.array(np.zeros((m, m)), dtype=np.float32, order='F')

    #1
    a = np.array(np.random.rand(m, n)*10, dtype=np.float32, order='C')
    b = np.array(np.random.rand(n, m)*10, dtype=np.float32, order='C')

    t0= time.time()
    c[:]= np.dot(a,b)
    # np.dot(a,b,out= c) # only for C-Array !
    print (time.time()-t0)

    del a
    del b
    # del c

    #2
    a = np.array(np.random.rand(m, n)*10, dtype=np.float32, order='C')
    b = np.array(np.random.rand(n, m)*10, dtype=np.float32, order='F')

    t0= time.time()
    c[:]= np.dot(a,b)
    # np.dot(a,b,out= c) # only for C-Array !
    print (time.time()-t0)

    del a
    del b
    # del c

    #3
    a = np.array(np.random.rand(m, n)*10, dtype=np.float32, order='F')
    b = np.array(np.random.rand(n, m)*10, dtype=np.float32, order='C')

    t0= time.time()
    c[:]= np.dot(a,b)
    # np.dot(a,b,out= c) # only for C-Array !
    print (time.time()-t0)

    del a
    del b
    # del c

    #4
    a = np.array(np.random.rand(m, n)*10, dtype=np.float32, order='F')
    b = np.array(np.random.rand(n, m)*10, dtype=np.float32, order='F')

    t0= time.time()
    c[:]= np.dot(a,b)
    # np.dot(a,b,out= c) # only for C-Array !
    print (time.time()-t0)

    del a
    del b
    # del c

    print 'done'

if __name__ == '__main__':
    test_order_()

还发现了一些关于 numpy.dot 复制和fast_dot的信息

dot 的内部运作有点模糊,因为它试图使用 BLAS 优化例程,有时需要数组副本 使用 Fortran 顺序

还有一些performance tips 这很奇怪,但我每次运行示例时都无法重现结果。(也许在重新运行一些数据之前?)

【问题讨论】:

  • 如果您在代码中自动且任意地在 C 样式和 FORTRAN 样式之间更改数组的顺序,那么您将严重混淆人们。
  • 如果只是二维的,那么你可以根据矩阵的形状用.T方法变换矩阵,然后再变换回来吗?我可以想象在某些情况下它可能值得做。
  • @Ffisegydd 该顺序仅影响内存布局。 API 保持不变。
  • @otterb 矩阵的转置只会创建一个视图,因此内存布局是相同的。
  • 内存分析器只在每行之后报告内存。中间 numpy 的胆量中的任何内容都不会被看到。此外,最好使用 out 参数:np.dot(a, b, out=c)

标签: python arrays numpy cuda matrix-multiplication


【解决方案1】:

性能取决于您拥有的底层线性代数库。

# ORDER C-C    
In [6]: %timeit a.dot(b)
10 loops, best of 3: 87.6 ms per loop

# ORDER C-F
In [8]: %timeit a.dot(b)
10 loops, best of 3: 87.8 ms per loop

# ORDER F-C
In [10]: %timeit a.dot(b)
10 loops, best of 3: 90.1 ms per loop

# ORDER F-F
In [12]: %timeit a.dot(b)
10 loops, best of 3: 90 ms per loop

我正在使用在这台机器上使用 SSE3 编译的 ATLAS,如 np.show_config() 所见。重新运行计算表明两者之间没有统计差异。事实上,没有区别,因为库在执行产品之前会复制数组。所述复制需要 650 µs(包括 Python 开销),低于您所拥有的时间。随着矩阵的增长,点积占主导地位,您看不到复制效果。如果您使用较小的矩阵,Python 开销会掩盖任何影响。

如果您监控内存并使用非常大的数组,您可以看到实际发生的副本。

【讨论】:

  • "但是,请确保您的输出数组是 Fortran 排序的,否则将进行复制。"或者你的意思是另一个副本?
【解决方案2】:

numpy 数组的顺序如何影响乘法速度?以及如何根据矩阵的大小自动选择它?

我怀疑这根本不重要。结果的变化可能归结为 GC 或其他进程占用 CPU。这是禁用 GC 后的我的:

m= 1024*10
n= 1024*1
9.80310893059
9.8962469101
9.80171990395
9.8697450161
9.79560208321


m= 1024*1
n= 1024*10  
0.977525949478
1.00992393494
1.0397670269
0.990566015244
0.986133098602

看起来一切都在方差范围内(前两个测量的是相同的东西,因为“C”是默认值)。

【讨论】:

  • 我认为这取决于cpu缓存,尝试增加矩阵大小。
  • @mrgloom,我达到了 3GB 阵列,但仍在差异范围内:38.1636929512 37.9753510952 38.0870800018 38.0148370266 39.0343549252
猜你喜欢
  • 2013-11-19
  • 2014-06-05
  • 2018-01-24
  • 1970-01-01
  • 2011-11-14
  • 2020-07-03
  • 2014-03-19
  • 1970-01-01
  • 2022-12-13
相关资源
最近更新 更多