std::array 与数组性能
Posted
技术标签:
【中文标题】std::array 与数组性能【英文标题】:std::array vs array performance 【发布时间】:2015-07-27 14:27:33 【问题描述】:如果我想构建一个非常简单的数组,比如
int myArray[3] = 1,2,3;
我应该改用std::array
吗?
std::array<int, 3> a = 1, 2, 3;
使用 std::array 比普通的有什么优势?它的性能更高吗?只是更容易处理复制/访问?
【问题讨论】:
用 std:: 定义多维数组会很困难 @goGud:不难,只是更冗长。 指针衰减、引用等...,很多关于 c 数组的事情都很奇怪。在 c 数组的情况下,迭代器可能是一个指针,for (auto i = ++std::begin(myArray); . . .
甚至可能无法编译(似乎基本类型的临时变量不是可变的,至少在 clang 6 中不是)
初始化也神奇地不同:struct Direction int32_t dw; int32_t dh; ;
和 static const Direction DIRECTIONS[DIRECTIONS_COUNT] -1, 1, 0,1, 1,1 , 1, 0 , 1,-1, 0,-1 , -1,-1, -1,0 ;
编译。但是,如果您更改为具有相同初始化程序列表的std::array<Direction,DIRECTIONS_COUNT>
,您会突然收到“初始化程序太多”错误。 (VS 2019 社区语言 = C++17)
@Marc.2377 std::array
只是 C 样式数组的包装器。如果我没记错的话,在某些早期版本的 C++ 中,您无法使用单括号对其进行初始化。但是,不要相信我的话,因为我没有太多使用它们
【参考方案1】:
使用
std::array
与普通的相比有什么优势?
它具有友好的值语义,因此可以按值传递给函数或从函数返回。它的界面可以更方便地查找大小,并与 STL 风格的基于迭代器的算法一起使用。
性能更好吗?
应该完全一样。根据定义,它是一个简单的聚合,包含一个数组作为其唯一成员。
只是更容易处理复制/访问?
是的。
【讨论】:
【参考方案2】:std::array
是一个非常薄的 C 风格数组的包装器,基本上定义为
template<typename T, size_t N>
struct array
T _data[N];
T& operator[](size_t);
const T& operator[](size_t) const;
// other member functions and typedefs
;
它是一个aggregate,它允许您像使用基本类型一样使用它(即您可以按值传递、分配等,而标准 C 数组不能分配或直接复制到另一个数组) .你应该看看一些标准实现(从你喜欢的IDE跳转到定义或直接打开<array>
),它是一个非常容易阅读和理解的C++标准库。
【讨论】:
轻微修正,在文档中定义为结构体:en.cppreference.com/w/cpp/container/array @Overt_Agent 谢谢,已更正(尽管class...public:...
具有与结构相同的访问规则;)【参考方案3】:
std::array
被设计为 C 数组的零开销包装器,使其具有“正常”值,类似于其他 C++ 容器的语义。
您应该不会注意到运行时性能的任何差异,同时您仍然可以享受额外的功能。
如果您手头有 C++11 或 boost,则使用 std::array
而不是 int[]
样式数组是个好主意。
【讨论】:
【参考方案4】:性能更好吗?
应该完全一样。根据定义,它是一个简单的聚合,包含一个数组作为其唯一成员。
情况似乎更复杂,因为 std::array
与 C-array 相比并不总是产生相同的汇编代码,具体取决于特定平台。
我在godbolt上测试了这个具体情况:
#include <array>
void test(double* const C, const double* const A,
const double* const B, const size_t size)
for (size_t i = 0; i < size; i++)
//double arr[2] = 0.e0;//
std::array<double, 2> arr = 0.e0;//different to double arr[2] for some compiler
for (size_t j = 0; j < size; j++)
arr[0] += A[i] * B[j];
arr[1] += A[j] * B[i];
C[i] += arr[0];
C[i] += arr[1];
GCC 和 Clang 为 C 数组版本和 std::array
版本生成相同的汇编代码。
MSVC 和 ICPC 会为每个数组版本生成不同的汇编代码。 (我用-Ofast
和-Os
测试了ICPC19;MSVC -Ox
和-Os
)
我不知道为什么会这样(我确实希望 std::array 和 c-array 的行为完全相同)。可能采用了不同的优化策略。
作为额外的一点: ICPC 中似乎有一个错误
#pragma simd
在某些情况下使用 c 数组时进行矢量化
(c-array 代码产生错误的输出;std::array
版本工作正常)。
不幸的是,我还没有一个最小的工作示例,因为我在优化一段相当复杂的代码时发现了这个问题。
当我确定我没有误解有关 C-array/std::array
和 #pragma simd
的某些内容时,我将向英特尔提交错误报告。
【讨论】:
可以认为是编译器的bug吗?【参考方案5】:std::array
具有值语义,而原始数组则没有。这意味着您可以复制 std::array
并将其视为原始值。您可以按值或引用作为函数参数接收它们,也可以按值返回它们。
如果您从不复制std::array
,则与原始数组相比没有性能差异。如果您确实需要制作副本,那么 std::array
会做正确的事情,并且仍然应该提供相同的性能。
【讨论】:
【参考方案6】:使用std::array
和c array
您将获得相同的性能结果
如果你运行这些代码:
std::array<QPair<int, int>, 9> *m_array=new std::array<QPair<int, int>, 9>();
QPair<int, int> *carr=new QPair<int, int>[10];
QElapsedTimer timer;
timer.start();
for (int j=0; j<1000000000; j++)
for (int i=0; i<9; i++)
m_array->operator[](i).first=i+j;
m_array->operator[](i).second=j-i;
qDebug() << "std::array<QPair<int, int>" << timer.elapsed() << "milliseconds";
timer.start();
for (int j=0; j<1000000000; j++)
for (int i=0; i<9; i++)
carr[i].first=i+j;
carr[i].second=j-i;
qDebug() << "QPair<int, int> took" << timer.elapsed() << "milliseconds";
return 0;
你会得到这些结果:
std::array<QPair<int, int> 5670 milliseconds
QPair<int, int> took 5638 milliseconds
Mike Seymour 是对的,如果你可以使用std::array
,你应该使用它。
【讨论】:
以上是关于std::array 与数组性能的主要内容,如果未能解决你的问题,请参考以下文章
C++ STL应用与实现5: 如何使用std::array (since C++11)
std::vector<std::array<T, N>> 或 std::array<std::vector<T>,N> 类型的数组如何存储在内存中?