【问题标题】:Many stack allocations vs. dynamic allocation许多堆栈分配与动态分配
【发布时间】:2012-03-02 18:09:00
【问题描述】:

在第二维的大小已知的多维数组中(尽管每个第一维不同),将这些数组实际构建为硬编码会更快性能 :

int* number[1000];

int secondDim[1];
number[0] = secondDim;

int secondDimTwo[2];
number[1] = secondDim;

等等。等等 1000 次(我知道,我知道)

或动态分配每个第二维度:

for(int i = 0; i < 1000; i++)
    number[i] = new int[i+1];

我只是想在这里围绕一个概念。

【问题讨论】:

    标签: c++ arrays dynamic heap-memory stack-memory


    【解决方案1】:

    作为一般规则,您可以假设堆栈分配会更快。请记住,堆栈的容量有限,过度使用大型堆栈分配的数组可能会导致,等等...堆栈溢出!

    无论如何,您的问题是有效的,但到目前为止我看到的解决方案非常有限。问题是,您可以轻松创建实用程序类型,它将充当三角矩阵,然后取决于特定用例是否将其存储在堆栈或堆中。观察:

    namespace meta
    {
        template <size_t N>
        struct sum
        {
            static const int value = (N + 1) * N / 2;
        };
    }
    
    template <size_t Size>
    struct MultiArray
    {
        // actual buffer
        int numbers[ meta::sum<Size>::value ];
    
        // run-time indexing
        int* getArray(size_t dimensions)
        {
            // get sum of (dimensions-1)
            size_t index = (dimensions * (dimensions-1)) >> 1;
            return &numbers[index];
        }
    
        // compile-time indexing
        template <size_t dimensions>
        int* getArray()
        {
            size_t index = meta::sum<dimensions - 1>::value ;
            return &numbers[ index ];
        }
    
        int* operator[](size_t index)
        {
            return getArray(index);
        }
    };
    

    现在由您决定将其存储在哪里。

    MultiArray<1000> storedOnStack;
    MultiArray<1000>* storedOnHeap = new MultiArray<1000>();
    

    你必须访问内部数组:

    int* runTimeResolvedArray = storedOnStack.getArray(10);
    int* compileTimeResolvedArray = storedOnStack.getArray<10>();
    int* runTimeResolvedArray2 = storedOnStack[10];
    storedOnStack[10][0] = 666;
    

    希望这会有所帮助!

    编辑: 我还必须说我不喜欢“堆栈分配”这个词。这是误导。堆栈分配本质上只是堆栈指针寄存器的增加。因此,如果您在堆栈上“分配”100 个字节,则指针将增加 100 个字节。但是如果你在堆上分配 100 个字节,那么它就变得复杂了——当前分配器必须找到合适的空内存空间,更新分配映射等等。

    如果是一次性分配 - 继续在堆上进行,动态分配的开销不会很明显。但如果每秒执行多次,则选择堆栈分配。此外,堆栈数组的访问速度可能更快,因为堆栈内容更有可能在缓存中。但显然非常大的数组不适合缓存。所以,答案是:profile.

    【讨论】:

      【解决方案2】:

      直到你实际配置过,并且知道动态分配 是个问题:

      std::vector<std::vector<int> > number;
      for ( size_t i = 0; i < 1000; ++ i ) {
          number.push_back( std::vector<int>( i + 1 );
      }
      

      或者(可能稍微快一点,特别是如果你的编译器没有 移动语义呢):

      std::vector<std::vector<int> > number( 1000 );
      for ( size_t i = 0; i != number.size() ; ++ i ) {
          number[i].resize( i + 1 );
      }
      

      如果这确实会导致性能问题,或者可能只是为了更好 封装,您可以编写一个表示数据的简单类 结构:

      class TriangularMatrix
      {
          std::vector<int> myData;
          size_t mySize;
      
          size_t rowIndex( size_t i ) const
          {
              return (i * (i + 1)) / 2;
          }
      public:
          TriangularMatrix( size_t size )
              : myData( rowIndex( size ) )
              , mySize( size )
          {
          }
          int* operator[]( size_t i )  // Returns row
          {
              assert( i < mySize );
              return &myData[rowIndex( i )];
          }
          int const* operator[]( size_t i ) const  // Returns row
          {
              assert( i < mySize );
              return &myData[rowIndex( i )];
          }
          int& operator( int i, int j )  // indexation
          {
              assert( j <= i );
              return operator[]( i )[ j ];
          }
          int const& operator( int i, int j ) const  // indexation
          {
              assert( j <= i );
              return operator[]( i )[ j ];
          }
      };
      

      事实上,我认为这样的东西会更干净,更 可读。

      【讨论】:

      • 将Row 类定义为myData 的切片而不是返回原始int* 会更简洁。它不仅会使注释变得多余,而且还可以确保正确的边界检查。
      • 向/从向量追加/访问项目比向/从堆栈分配/访问更快?为了论证起见,假设需要速度。
      • @MatthieuM。同意。但是代码示例已经看起来有点长了。
      • @user1201584 访问权限应该差不多;这在很大程度上取决于编译器优化器。无论如何,指数计算很可能占主导地位。 (取决于机器,可能值得为索引创建缓存。尽管在大多数现代机器上,我怀疑(i + (i + 1)) / 2 比内存访问快。)并且(单个)向量的尺寸与正确的大小开始。使用封装,几乎不可能避免单一的动态分配,而没有封装,这样的事情几乎不可能维护。
      【解决方案3】:

      更好的方法是一次性分配整个数组,这样所有数据都是连续的(更好的缓存)。

      int * arr; arr = malloc(sizeof(int)*sum(1,n));你必须定义 sum 函数。

      只需分配一个二维数组(第一个暗淡)并将指针设置到 arr 中的正确位置。

      【讨论】:

      • 没有。你永远不想使用malloc(也不想使用new[])。应用程序代码中的唯一一次动态分配适用于单个元素;一旦存在多个元素,请使用std::vector。
      • 分配给大型数组的速度和使用堆栈一样快吗?访问速度也一样快?在我的情况下,速度是唯一的优先事项
      【解决方案4】:

      如果你已经知道你需要的数组的大小,你应该使用堆栈上的数组。
      请注意,通常动态分配比堆栈分配更昂贵。
      如果性能差异对于您的情况足够显着,则只能通过分析来确定。

      另外,避免动态分配,因为它们更容易出错,除非您使用基于 RAII 的某种资源管理。

      【讨论】:

      • 有没有比我上面做的硬编码更快的实际编码方法?
      猜你喜欢
      • 2023-04-02
      • 2011-10-06
      • 1970-01-01
      • 2019-05-17
      • 1970-01-01
      • 2012-03-19
      • 1970-01-01
      • 2011-05-28
      相关资源
      最近更新 更多