代码之家  ›  专栏  ›  技术社区  ›  Holden

为什么sklearn在CPU上比在GPU上更快?

  •  2
  • Holden  · 技术社区  · 7 年前

    但是,我得到了奇怪的结果。 看看我画的图。

    Processing Time Comparison

    您可以看到scikit learn的结果,它比theano(GPU)更快。 我检查其经过时间的程序是从一个包含n*40个元素的矩阵计算欧几里德距离矩阵。

    points = T.fmatrix("points")
    edm = T.zeros_like(points)
    
    def get_point_to_points_euclidean_distances(point_id):
        euclideans = (T.sqrt((T.sqr(points- points[point_id, : ])).sum(axis=1)))
    
        return euclideans
    
    def get_EDM_CPU(points):
        EDM = np.zeros((points.shape[0], points.shape[0])).astype(np.float32)
        for row in range(points.shape[0]):
            EDM[row, :] = np.sqrt(np.sum((points - points[row, :])**2, axis=1))
    
        return EDM
    
    def get_sk(points):
        EDM = sk.pairwise_distances(a, metric='l2')
    
        return EDM
    
    seq = T.arange(T.shape(points)[0])
    (result, _) = theano.scan(fn = get_point_to_points_euclidean_distances, \
    outputs_info = None , \
    sequences = seq)
    
    get_EDM_GPU = theano.function(inputs = [points], outputs = result, allow_input_downcast = True)
    

    我认为GPU比sci kit learn慢的原因可能是传输时间。所以我用nvprof命令分析了GPU。然后我得到了这个。

    ==27105== NVPROF is profiling process 27105, command: python ./EDM_test.py
    Using gpu device 0: GeForce GTX 580 (CNMeM is disabled, cuDNN not available)
    data shape :  (10000, 40)
    get_EDM_GPU elapsed time :  1.84863090515 (s)
    get_EDM_CPU elapsed time :  8.09937691689 (s)
    get_EDM_sk elapsed time :  1.10968112946 (s)
    ratio :  4.38128395145
    ==27105== Profiling application: python ./EDM_test.py
    ==27105== Warning: Found 9 invalid records in the result.
    ==27105== Warning: This could be because device ran out of memory when profiling.
    ==27105== Profiling result:
    Time(%)      Time     Calls       Avg       Min       Max  Name
     71.34%  1.28028s      9998  128.05us  127.65us  128.78us  kernel_reduce_01_node_316e2e1cbfbe8cfb8e4a101f329ffeec_0(int, int, float const *, int, int, float*, int)
     19.95%  357.97ms      9997  35.807us  35.068us  36.948us  kernel_Sub_node_bc41b3f8f12c93d29f2c4360ad445d80_0_2(unsigned int, int, int, float const *, int, int, float const *, int, int, float*, int, int)
      7.32%  131.38ms         2  65.690ms  1.2480us  131.38ms  [CUDA memcpy DtoH]
      1.25%  22.456ms      9996  2.2460us  2.1140us  2.8420us  kernel_Sqrt_node_23508f8f49d12f3e8369d543f5620c15_0_Ccontiguous(unsigned int, float const *, float*)
      0.12%  2.1847ms         1  2.1847ms  2.1847ms  2.1847ms  [CUDA memset]
      0.01%  259.73us         5  51.946us     640ns  250.36us  [CUDA memcpy HtoD]
      0.00%  17.086us         1  17.086us  17.086us  17.086us  kernel_reduce_ccontig_node_97496c4d3cf9a06dc4082cc141f918d2_0(unsigned int, float const *, float*)
      0.00%  2.0090us         1  2.0090us  2.0090us  2.0090us  void copy_kernel<float, int=0>(cublasCopyParams<float>)
    

    { 1.248 [us], 131.38 [ms] }

    传输[CUDA memcpy HtoD]执行5x { min: 640 [ns], max: 250.36 [us] }

    传输时间约为131.639 ms(131.88 ms+259.73 us)。

    1 回复  |  直到 7 年前
        1
  •  2
  •   user3666197    7 年前

    是什么让scikit学习(在纯CPU方面)如此快?

    我最初的候选人包括:

    • 高效利用可用CPU核 L1-/L2-最快[ns]距离内的大小
    • numpy 矢量化执行 对CPU缓存线友好
    • 它很小,可以完全不从缓存中逐出
    • 可能会享受更好的时机 努比 .astype() 转换(测试)

    • 大于几KB的数据结构仍然需要支付大约数百个[ns]的GPU-SM/GDDR-MEM距离,接近[us]-v/s-与CPU/L1/L2/L3/DDRx上的小单元约几十个[ns]相比)参考中的计时细节&燃气轮机&燃气轮机; https://stackoverflow.com/a/33065382
    • 由于此任务的数据点重用率明显较低,并且数据集大小超出了GPU/SM硅限制,因此无法享受GPU/SMX的强大功能,这在任何类型的GPU内核设计尝试和调整中都会导致GPU/SM寄存器容量溢出

    如果进行足够密集的卷积再处理操作(如在大规模/高分辨率图像处理中),GPU-s可以展示其最佳性能 [m,n,o] m*n*o 常量值可以驻留在SM的本地,在一组可用的SMX-SM_寄存器内,并且如果GPU内核启动器通过3D tblock/grid处理布局几何结构进行最佳调整,以便全局内存访问延迟处于最佳屏蔽性能,让所有GPU线程在硬件扭曲对齐的SMx中执行:WarpScheduler循环线程调度能力(如果GPU内核代码中的执行路径不同,第一次从循环切换到贪婪的WarpSchedule模式将失去整个战斗)。