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&lt;Direction,DIRECTIONS_COUNT&gt;,您会突然收到“初始化程序太多”错误。 (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跳转到定义或直接打开&lt;array&gt;),它是一个非常容易阅读和理解的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];
  

GCCClang 为 C 数组版本和 std::array 版本生成相同的汇编代码。

但是,

MSVCICPC 会为每个数组版本生成不同的汇编代码。 (我用-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::arrayc 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 与数组性能的主要内容,如果未能解决你的问题,请参考以下文章

用于连续内存的 std::array 与 C 样式数组

C++ STL应用与实现5: 如何使用std::array (since C++11)

从 std::array 获取对原始数组的引用

为啥我可以从 初始化常规数组,而不是 std::array

同时使用托管数组和 std:array 不兼容

std::vector<std::array<T, N>> 或 std::array<std::vector<T>,N> 类型的数组如何存储在内存中?