代码之家  ›  专栏  ›  技术社区  ›  Cedric H.

选择性能最好的容器(数组)

c++
  •  11
  • Cedric H.  · 技术社区  · 16 年前

    这是我关于容器的一个大问题,特别是数组。

    我正在写一个物理代码,它主要处理一个大的(>1000000)的“粒子集”(有6个粒子) double 坐标)。我正在寻找实现一个类的最佳方法(就性能而言),该类将包含这些数据的容器,并为这些数据提供操作原语(例如实例化, operator[]

    如何使用此集合有一些限制:

    • 它可以看作是一个大的二维数组,由N(例如1 000 000)行和6列组成(每个列存储一维坐标)
    • 数组在一个大循环中被操纵,每个“粒子/线”都被访问,并用它的坐标进行计算,结果被存储回这个粒子,每个粒子如此,大循环的每次迭代也是如此。
    • 执行期间不会添加或删除新元素

    第一个结论,由于对元素的访问基本上是通过使用 [] ,我认为我应该使用一个普通的动态数组。

    std::vector double** array2d = new ..., loop of new, etc 被排除在外。

    那么使用它是个好主意吗 std::vector<double>

    如果我使用 std::vector<std::vector<double> > my_array 可以索引为 my_array[i][j] ,或者这是一个坏主意,使用它会更好 std::vector<double> other_array other_array[6*i+j] .

    也许这可以提供更好的性能,特别是列的数量是固定的,并且从一开始就知道。

    如果您认为这是最好的选择,是否可以将此向量包装为可以使用定义为的索引运算符访问的方式 other_array[i,j] // same as other_array[6*i+j] 没有开销(比如每次访问时的函数调用)?

    另一个选择,我目前正在使用的是使用闪电战,特别是 blitz::Array :

    typedef blitz::Array<double,TWO_DIMENSIONS> store_t;
    store_t my_store;
    

    我的元素是这样访问的: my_store(line, column); .

    你认为闪电战没问题,还是对我没用?

    非常感谢你在这个问题上的帮助!

    编辑:

    • 使用结构 particle
    • 使用 vector 或者 deque 微粒 结构或数组。然后最好用迭代器遍历它们,这样以后就可以从一个迭代器切换到另一个迭代器。

    Blitz::TinyVector<double,6> 而不是一个结构。

    5 回复  |  直到 15 年前
        1
  •  4
  •   Matthieu M.    16 年前

    struct :

    struct Particle { /* coords */ };
    

    然后我们可以做一个简单的一维数组 Particles .

    deque ,因为这是默认容器,但您可能希望尝试 vector ,只是1.000.000个粒子意味着一块几兆字节的碎片。它应该保持住,但如果它增长的话,可能会给你的系统带来压力 德克 将分配几个块。

    警告 :

    德克 道路,禁止使用 operator[] 更喜欢使用迭代风格。如果您真的需要随机访问并且它对性能非常敏感 矢量 应该更快。

        2
  •  8
  •   sbi    16 年前

    std::vector<double> ?

    通常情况下 std::vector std::vector<>::reserve() std::vector<>::resize() 以避免在填充向量时重新分配。是否有其他更好的容器可以通过 测量 . 只有通过测量。但首先要衡量容器所涉及的任何内容(填充、访问元素)是否值得优化。

    std::vector<std::vector<double> > [...]?

    std::vector<particle> particle 结构是否包含六个值?即使我理解错误,你也应该在一维容器周围写一个二维包装。然后以行或列的形式对齐数据—使用访问模式会更快。

    你认为闪电战没问题,还是对我没用?

        3
  •  2
  •   Didier Trosset    16 年前

    从容器中选择的第一条规则是使用 std::vector . 然后,只有在代码完成并且可以实际测量性能之后,才能尝试其他容器。但首先要坚持向量。(1)使用 reserve() (从一开始)

    那么,你不应该使用 std::vector<std::vector<double> > . 你知道数据的大小:是6倍。它不需要是动态的。它是固定不变的。您可以定义一个结构来容纳粒子成员(六个双粒子),也可以简单地键入def: typedef double particle[6] . 然后,使用粒子向量: std::vector<particle> .

    此外,由于您的程序按顺序使用矢量中包含的粒子数据,您将以最佳性能利用现代CPU缓存预读功能。

        4
  •  1
  •   user180326 user180326    16 年前

    不要 宣布 std::vector<std::vector<double> > . 你在分配一个 vector

        5
  •  1
  •   Tony Delroy    16 年前

    如果您认为这是最好的选择,那么是否可以将此向量包装为可以使用定义为other\u array[i,j]//与other\u array[6*i+j]相同的索引运算符进行访问,而不会产生开销(例如每次访问时的函数调用)?

    ( other_array[i,j] 不会很好地工作,因为i,j使用逗号运算符计算“i”的值,然后丢弃该值并计算并返回“j”,所以它等价于 other_array[i] ).

    other_array[i][j]
    other_array(i, j)  // if other_array implements operator()(int, int),
                       // but std::vector<> et al don't.
    other_array[i].identifier // identifier is a member variable
    other_array[i].identifier() // member function getting value
    other_array[i].identifier(double) // member function setting value
    

    所以,一个很好的测试:如果你发现自己写的代码 other_array[i][3] 你已经决定“3”是速度的两倍,而且 other_array[i][5] other_array[i].speed .acceleration . 这样其他开发人员就可以阅读和理解它了,你就不太可能犯意外错误了。另一方面,如果你在这6个元素上迭代,对每一个元素都做完全相同的事情,那么你可能真的希望粒子持有一个double[6],或者提供一个 operator[](int) . 两种方法都可以:

    struct Particle
    {
        double x[6];
        double& speed() { return x[3]; }
        double speed() const { return x[3]; }
        double& acceleration() { return x[5]; }
        ...
    };
    

    顺便说一句 vector<vector<double> > 可能代价太高的是,每6个double的集合都将在堆上分配,而对于快速分配和取消分配,许多堆实现使用固定大小的bucket,因此您的小请求将被舍入到下一个大小:这可能是一个很大的开销。外部向量还需要记录指向该内存的额外指针。此外,堆分配和释放相对较慢—在您的情况下,您只会在启动和关闭时进行,但没有特别的意义使程序无缘无故地变慢。更重要的是,堆上的区域可能就在内存中,因此操作符[]可能会出现缓存错误,从而拉入比需要更多不同的内存页,从而降低整个程序的速度。换句话说,向量连续存储元素,但指向的向量可能不连续。