9

通常,我想知道标准模板库是否会在数值/科学计算的代码中产生性能/速度开销。

例如。将数组声明为

double 2dmatrix [10][10]

会给我更多的表现

std::vector<std::vector<double> > 2dmatrix(10,std::vector<double>(10,0.0))

?

我也很欣赏一些一般性的想法,关于 C 在科学计算方面是否比 C++ 具有更好的性能。我已经使用 STL 以非常面向对象的风格编写了我的代码,并且经常使用 C++11。我开始考虑是否应该开始研究纯 C,如果它运行得更快。

欢迎对此提出任何想法。

4

7 回答 7

14

鉴于它提供的抽象,C++std::vector尽可能高效:堆栈上的 3 个指针,以及动态分配的数据,在线性增长方案中平均每个元素重新分配 1 次(因为调整大小比按比例扩展容量,a 1.5 到 2 的因数)。

使用malloc()and的 C 等价物realloc()至少同样昂贵,而且更麻烦(手动调整大小等)。此外,std::vector允许通过特殊分配器(基于池、堆栈分配等)进行用户定义的性能调整,这在 C++11 中并不像在 C++98 中那样难以使用。

如果您不需要动态调整大小,您可以用 C 和 C++ 编写静态数组(或std::arrayC++)。

一般来说,对于高性能计算,C++ 具有更大的优化潜力,特别是通过使用可以内联的函数对象(与常规 C 函数指针相比​​)。典型的例子是排序

int comp( const void* a, const void* b ) {
    return /* your comparison here */;
}

// C style sorting
qsort( arr, LARGE_SIZE, sizeof( int ), comp ); 
                                       ^^^^ <---- no-inlining through function pointer

// C++11 style sorting (use hand-made function object for C++98
std::sort(std::begin(arr), std::end(arr), [](auto a, auto b) { 
    return comp(&a, &b);
           ^^^^ <----- C++11 lambdas can be fully inlined
});
于 2013-08-20T18:43:25.633 回答
9

std::vector 的开销是:

  • 堆栈上的 3 个指针
  • 动态分配(懒惰,即在需要之前它不会分配任何东西)

在某些情况下(对于少量数据),堆栈分配的数组可能会更快。为此,您可以使用std::array<T, Length>.

如果您需要二维网格,我会将数据分配在单个向量中:std::vector<T>(width * height);. 然后你可以编写一些辅助函数来通过 x 和 y 坐标获取元素。(或者你可以写一个包装类。)

于 2013-08-20T18:37:12.760 回答
3

人们会说“这取决于你在做什么”。

他们是对的。

这里有一个示例,其中使用传统设计的程序std::vector通过一系列六个阶段进行性能调整,其执行时间从每工作单位的 2700 微秒减少到 3.7 微秒,加速因子为 730 倍。

要做的第一件事是注意到大部分时间都花在了增长数组和从中删除元素上。因此使用了不同的数组类,这大大减少了时间。

所做的第二件事是注意到大部分时间仍在与阵列相关的活动中。因此,数组被完全消除,而使用链表,产生了另一个巨大的加速。

然后其他事情占用了剩余时间的很大一部分,例如newing 和deleteing 对象。然后这些对象在空闲列表中被回收,产生另一个巨大的加速。经过几个阶段后,决定停止尝试,因为越来越难找到改进的地方,而且加速被认为是足够的。

关键是,不要只是选择强烈推荐的东西,然后寄希望于最好的。而是以一种或另一种方式构建它,然后像这样进行性能调整,并愿意根据您看到的大部分时间花费在您的数据结构设计中进行重大更改。并迭代它。您可以将存储方案从 A 更改为 B,然后再从 B 更改为 C。这完全没问题。

于 2013-08-20T19:17:57.217 回答
3

如果您没有理由调整数组的大小,并且在编译期间知道它的大小(就像您在第一个示例中所做的那样),那么 STL 模板的更好选择是std::array模板。它为您提供了与 C 样式数组相同的所有优点。

double 2dmatrix[10][10];

// would become

std::array<std::array<double, 10>, 10> 2dmatrix;
于 2013-08-20T18:39:03.907 回答
3

如果您事先知道尺寸并且性能是瓶颈 -std::array从 C++11 开始使用。它的性能与 C 样式数组完全相同,因为在内部它看起来像

template<typename T, int N>
struct array {
  T _data[N];
};

这是在现代 C++ 中使用堆栈分配数组的首选方式。如果你有一个现代编译器,千万不要使用 C 风格的数组。

于 2013-08-20T18:44:15.533 回答
1

在科学计算中,错误和次优代码尤其令人沮丧,因为大量数据被错误处理并浪费了宝贵的时间。

std::vector根据您对其内部运作的了解,可能是您的瓶颈或表现最好的人。特别注意reserve(), insert(), erase(); 如果您的程序是线程化的,请考虑学习对齐和处理器缓存。

想想如果您尝试自己完成所有内存管理,特别是当您逐步向软件添加功能时,您必须花时间确保一致性 - 以及后来寻找错误 - 。归根结底,std::vector 的开销将是您的问题中最少的。

于 2013-08-21T03:17:18.930 回答
0

对于科学计算,最好使用专用的 C++ 矩阵库,例如Armadillo。这不仅为您提供了快速的数组处理,而且还提供了许多经过彻底调试的线性代数运算。

除了性能原因之外,使用专用的 C++ 矩阵库还可以让您大大减少代码的冗长程度,减少错误,从而加快开发速度。一个例子是,使用 C++ 矩阵库,您无需担心内存管理。

最后,如果您真的需要进入低级(即直接通过指针使用内存),C++ 允许您“下降”到 C 级。在 Armadillo 中,这是通过.memptr()成员函数完成的。

于 2013-08-21T03:20:32.370 回答