C++ 修改指针中的值非常慢?

C++ 修改指针中的值非常慢?,c++,arrays,pointers,optimization,C++,Arrays,Pointers,Optimization,我正在处理存储在阵列中的大量数据,并试图优化访问和修改这些数据所需的时间。我使用的是窗口、C++和VS2015(发布模式)。 我运行了一些测试,但并不真正理解我得到的结果,所以我希望能得到一些帮助来优化我的代码 首先,假设我有以下课程: class foo { public: int x; foo() { x = 0; } void inc() { x++; } int X() {

我正在处理存储在阵列中的大量数据,并试图优化访问和修改这些数据所需的时间。我使用的是窗口、C++和VS2015(发布模式)。 我运行了一些测试,但并不真正理解我得到的结果,所以我希望能得到一些帮助来优化我的代码

首先,假设我有以下课程:

class foo
{
public:
    int x;

    foo() 
    {
        x = 0;
    }

    void inc()
    {
        x++;
    }

    int X()
    {
        return x;
    }

    void addX(int &_x)
    {
        _x++;
    }

};
我首先将指向该类实例的1000万个指针初始化为相同大小的std::vector

#include <vector>
int count = 10000000;
std::vector<foo*> fooArr;
fooArr.resize(count);
for (int i = 0; i < count; i++)
{
     fooArr[i] = new foo();
}
#包括
整数计数=10000000;
std::载体fooArr;
fooArr.resize(计数);
for(int i=0;i
当我运行以下代码并分析完成所需的时间时,大约需要350毫秒(就我而言,这太慢了):

for(int i=0;iinc();//递增所有元素
}
为了测试增加一个整数的次数需要多长时间,我尝试了:

int x = 0;
for (int i = 0; i < count; i++)
{
    x++;
}
intx=0;
for(int i=0;i
返回inc()//仅增量第一个元素 } 我认为数组索引访问本身可能是瓶颈,但下面的代码使用X()//集合x }
在上一个例子中,我认为编译器可能对循环本身进行了一些隐藏的优化(因为在循环的每次迭代中x的值都是相同的,所以编译器可能跳过了不必要的迭代?)。因此,我尝试了以下方法,需要350毫秒才能完成:

int x;
for (int i = 0; i < count; i++)
{
    x = fooArr[i]->X(); //set x
}
int x;
for (int i = 0; i < count; i++)
{
    fooArr[i]->addX(x); //increment x inside foo function
}
intx;
for(int i=0;iaddX(x);//在foo函数中增加x
}
这一次又慢了,但可能只是因为我再次用指针递增一个整数

我也尝试了以下方法,它也会在350毫秒后返回:

for (int i = 0; i < count; i++)
{
    fooArr[i]->x++;
}
for(int i=0;ix++;
}
我被困在这里了吗?在向量中1000万个指针的范围内,~350ms是增加整数的绝对最快速度吗?还是我遗漏了一些明显的东西?我尝试了多线程(给每个线程一个不同的数组块来递增),一旦我开始使用足够的线程,这实际上需要更长的时间。也许这是因为我遗漏了其他一些明显的东西,所以现在我想远离多线程来保持简单

我也愿意尝试向量以外的容器,如果它能加快速度的话,但不管我最终使用什么容器,我都需要能够轻松地调整它的大小,删除元素,等等


我对C++相当陌生,所以任何帮助都会被理解的! 让我们从CPU的角度来看。 递增一个整数意味着我在CPU寄存器中有它,只是递增它。这是最快的选择。 我得到了一个地址(vector->member),我必须将它复制到寄存器,递增,然后将结果复制回地址。最糟糕的是:我的CPU缓存充满了向量指针,而不是向量成员指针。命中太少,缓存“加油”太多。 如果我能够将所有这些成员都放在一个向量中,那么CPU缓存命中的频率就会更高。

请尝试以下方法:

int count = 10000000;
std::vector<foo> fooArr;
fooArr.resize(count, foo());

for (auto it= fooArr.begin(); it != fooArr.end(); ++it) {
   it->inc();
} 
int count=10000000;
std::载体fooArr;
fooArr.resize(count,foo());
for(auto it=fooArr.begin();it!=fooArr.end();++it){
it->inc();
} 
新的
正在扼杀你,实际上你不需要它,因为
调整大小
如果它的大小更大,则在末尾插入元素(检查文档:)


另一件事是关于使用指针,IMHO应该避免,直到最后一刻,在这种情况下是不成功的。在这种情况下,性能应该快一点,因为您可以获得更好的引用局部性(请参阅缓存局部性)。如果它们是多态的或更复杂的,则可能会有所不同。

您应该查看汇编代码,看看它在每种情况下都进行了哪些优化。具有
x=fooArr[i]>x()的版本
可能正在优化整个循环,只是执行
x=fooArr[n-1]->x()那个普通的整数递增测试非常可疑,它很可能被优化为一些琐碎的东西。无论如何,为什么foos不直接在向量中,没有指针呢?我可以很容易地把你的速度分类:速度慢的写38Mb的内存,速度快的写4字节的内存。问题是缓存。您的简单增量循环将被任何合适的编译器替换为x=10000000,350ms主测试=每个循环35纳秒。很容易将一个缓存未命中映射回RAM,大约每100个循环一次。请注意,整数增量循环可能已经发生,但编译器会对其进行优化。我们真的需要看看OP测试台是如何标记代码的,以及他们过去知道的设置,以确定发生了什么。@NathanOliver我的基准测试一点也不奇怪。从字面上看,循环前后都只有一个“GetTickCount()”,然后我从第二个中减去第一个以得到毫秒数。@Tyson这让我相信编译器优化了for循环,因为它没有副作用。要确定更改
intx=0
易失性int x=0并查看时间是否发生变化。@NathanOliver添加了“volatile”,循环现在需要250毫秒,因此您似乎是正确的。@Tyson OK。好了。编译器真的很擅长避免它认为不需要做的事情,这使得微型基准测试变得困难。我认为他没有在他的基准测试中包括实例化,因此
新的
不包括在数字中。这是一个简单循环的优化问题,打破了比较。数组(即所有“新”数组)的创建被排除在我的基准时间之外,但存储值而不是指针肯定会提供很好的加速。现在,当使用代码时,我得到100毫秒,当使用常规索引迭代循环时,我得到80毫秒。我认为我的解决办法是t
for (int i = 0; i < count; i++)
{
    fooArr[i]->x++;
}
int count = 10000000;
std::vector<foo> fooArr;
fooArr.resize(count, foo());

for (auto it= fooArr.begin(); it != fooArr.end(); ++it) {
   it->inc();
}