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

当将向量存储为类属性时,为什么NumPy的矢量化评估速度较慢?

  •  2
  • IchKenneDeinenNamen  · 技术社区  · 1 年前

    我正在编写一个助手类来评估网格上的参数化函数。由于网格不会随着参数的变化而变化,我选择将其作为类属性一次性创建。

    然而,我意识到,与将网格作为全局变量相比,将网格作为类属性会导致性能显著下降。更有趣的是,2D网格的性能差异似乎随着1D网格的消失而消失。

    以下是一个演示该问题的最小“工作”示例:

    import numpy as np
    import time
    
    
    grid_1d_size = 25000000
    grid_2d_size = 5000
    x_min, x_max = 0, np.pi / 2
    y_min, y_max = -np.pi / 4, np.pi / 4
    
    # Grid evaluation (2D) with grid as class attribute
    
    class GridEvaluation2DWithAttribute:
        def __init__(self):
            self.x_2d_values = np.linspace(x_min, x_max, grid_2d_size, dtype=np.float128)
            self.y_2d_values = np.linspace(y_min, y_max, grid_2d_size, dtype=np.float128)
    
        def grid_evaluate(self):
            cost_values = np.cos(self.x_2d_values[:, None] * self.y_2d_values[None, :])
            return cost_values
    
    grid_eval_2d_attribute = GridEvaluation2DWithAttribute()
    initial_time = time.process_time()
    grid_eval_2d_attribute.grid_evaluate()
    final_time = time.process_time()
    print(f"2d grid, with grid as class attribute: {final_time - initial_time} seconds")
    
    # Grid evaluation (1D) with grid as global variable
    
    x_2d_values = np.linspace(x_min, x_max, grid_2d_size)
    y_2d_values = np.linspace(y_min, y_max, grid_2d_size)
    
    class GridEvaluation2DWithGlobal:
        def __init__(self):
            pass
    
        def grid_evaluate(self):
            cost_values = np.cos(x_2d_values[:, None] * y_2d_values[None, :])
            return cost_values
    
    grid_eval_2d_global = GridEvaluation2DWithGlobal()
    initial_time = time.process_time()
    grid_eval_2d_global.grid_evaluate()
    final_time = time.process_time()
    print(f"2d grid, with grid as global variable: {final_time - initial_time} seconds")
    
    # Grid evaluation (1D) with grid as class attribute
    
    class GridEvaluation1DWithAttribute:
        def __init__(self):
            self.x_1d_values = np.linspace(x_min, x_max, grid_1d_size, dtype=np.float128)
    
        def grid_evaluate(self):
            cost_values = np.cos(self.x_1d_values)
            return cost_values
    
    grid_eval_1d_attribute = GridEvaluation1DWithAttribute()
    initial_time = time.process_time()
    grid_eval_1d_attribute.grid_evaluate()
    final_time = time.process_time()
    print(f"1d grid, with grid as class attribute: {final_time - initial_time} seconds")
    
    # Grid evaluation (1D) with grid as global variable
    
    x_1d_values = np.linspace(x_min, x_max, grid_1d_size, dtype=np.float128)
    
    class GridEvaluation1DWithGlobal:
        def __init__(self):
            pass
    
        def grid_evaluate(self):
            cost_values = np.cos(x_1d_values)
            return cost_values
    
    grid_eval_1d_global = GridEvaluation1DWithGlobal()
    initial_time = time.process_time()
    grid_eval_1d_global.grid_evaluate()
    final_time = time.process_time()
    print(f"1d grid, with grid as global variable: {final_time - initial_time} seconds")
    

    这是我得到的输出:

    2d grid, with grid as class attribute: 0.8012442529999999 seconds
    2d grid, with grid as global variable: 0.20206781899999982 seconds
    1d grid, with grid as class attribute: 2.0631387639999996 seconds
    1d grid, with grid as global variable: 2.136266148 seconds
    

    如何解释这种性能差异?

    我将网格从类属性移动到全局变量。我希望这个变化对性能是中性的。然而,性能发生了重大变化。

    1 回复  |  直到 1 年前
        1
  •  2
  •   Yakov Dan    1 年前

    您与设置网格的数据类型不一致。

    比较

    self.x_2d_values = np.linspace(x_min, x_max, grid_2d_size, dtype=np.float128)
    self.y_2d_values = np.linspace(y_min, y_max, grid_2d_size, dtype=np.float128)
    

    具有

    x_2d_values = np.linspace(x_min, x_max, grid_2d_size)
    y_2d_values = np.linspace(y_min, y_max, grid_2d_size)
    
    

    如果未指定,则默认类型为 np.float32 ,比短4倍,通常快4倍 np.float128 ,这与您测量的时间相匹配。