为什么在C++中维护排序数组比Vector快

Why Maintaining Sorted Array is faster than Vector in C++

本文关键字:数组 Vector 排序 维护 C++ 为什么      更新时间:2023-10-16

我正在创建一个大小为100的数组和向量,并生成一个随机值,并试图将数组和向量保持为已排序。这是我的同一代码

vector<int> myVector;
int arr[SIZE];
clock_t start, finish;
int random;
for(int i=0; i<SIZE;i++)
{
    myVector.push_back(0);
    arr[i] = 0;
}
    //testing for Array
start = clock(); 
for(int i=0; i<MAX;++i)
{
    random = getRandom(); //returns rand() % 100
    for(int j=0; j<SIZE;++j){
        if(random > arr[j])
        {
            for(int k = SIZE - 1; k > j ; --k)
            {
                arr[k] = arr[k-1];
            }
            arr[j] = random;
            break;
        }
    }
}
finish = clock();
cout << "Array Time " << finish - start << endl;
//Vector Processing
start = clock();
for(int i=0; i<MAX;++i)
{
    random = getRandom(); //returns rand() % 100
    for(int j=0; j<SIZE;++j){
        if(random > myVector[j])
        {
            for(int k = SIZE - 1; k > j ; --k)
            {
                myVector[k] = myVector[k-1];
            }
            myVector[j] = random;
            break;
        }
    }
}
finish = clock();
cout << "Vector Time " << finish - start << endl;

输出如下:

阵列时间:5

矢量时间:83

我不明白为什么在这种情况下,向量和数组相比如此缓慢?这难道不与偏好向量而非数组的经验法则相矛盾吗。

请帮忙!

首先:编程中的许多经验法则不是为了组织几毫秒的性能,而是为了管理复杂性,从而避免错误。在这种情况下,它是关于执行范围检查的,就像大多数矢量实现在调试模式下所做的那样,而数组则没有。它还涉及到动态数组的内存管理——vector确实管理它的内存本身,而您必须在数组中手动执行,冒着引入内存泄漏的风险(是否忘记了delete[]或使用了delete?我是您!)。它还涉及到易用性,例如调整向量大小或在中间插入元素,这对于手动管理的数组来说是一项乏味的工作
换句话说,性能度量永远不会与经验法则相矛盾,因为经验法则永远不会以性能为目标。性能测量只能是遵守编码准则的少数可能原因之一。

乍一看,我猜您还没有启用优化。向量性能损失的主要来源将是许多向量实现为调试构建启用的索引检查。这些不会在优化的构建中发挥作用,所以这应该是您首先关心的问题经验法则:未启用优化的性能测量毫无意义

如果启用优化仍然显示阵列的性能更好,那么还有另一个区别:

数组存储在堆栈上,因此编译器可以直接使用地址并在compiletime计算地址偏移,而向量元素存储在堆上,编译器将不得不取消引用存储在向量中的指针。我希望优化器取消引用指针一次,然后计算从该点开始的地址偏移。不过,与compiletime计算的地址偏移相比,可能会有较小的性能损失,尤其是如果优化器可以稍微展开循环的话这仍然不违反经验法则,因为你在这里把苹果和梨进行比较。经验法则表明,

动态阵列相比,更喜欢std::vector,与固定数组相比,更愿意std::array

因此,要么使用动态分配的数组(请包括某种delete[]),要么将固定大小的数组与std::array进行比较。在C++14中,您必须在游戏中考虑新的候选者,即std::dynarray和C++14 VLA,它们是不可调整大小的、运行时长度与C的VLA相当的数组。

更新:正如评论中所指出的,优化器善于识别没有副作用的代码,比如对数组的操作,而这些操作你从来没有读过。std::vector的实现非常复杂,优化器通常不会看穿这几个间接层并优化掉所有插入,因此与向量的一些时间相比,数组的时间为零。在之后读取数组内容,循环将禁用这种粗鲁的优化。

向量类必须动态增长内存,这可能涉及不时复制整个内容。此外,它还必须为许多操作调用内部函数,比如重新分配。此外,它可能具有边界检查等安全功能。

同时,您的数组是预先分配的,并且您的所有操作都不会调用任何内部函数。

这是更多功能的间接费用。

谁说矢量在任何情况下都应该比数组快?您的数组不需要增长,这是一种特殊情况,数组确实更快!

因为数组是本机数据类型,而编译器可以直接从内存中操作它,所以它们由编译的exec内部管理。

另一方面,你得到的向量更像是一个类,我读到的模板,它需要通过另一个头文件和库进行一些管理。

本质上,本机数据类型可以在不包括任何头的情况下进行管理,这使得它们更容易从程序中操作,而不必使用外部代码。这使得向量时间的开销是程序需要查看代码并使用与向量数据类型相关的方法。

每次你需要向你的应用程序添加更多代码并从中操作时,它都会使你的应用性能下降

你可以在这里、这里和这里阅读