我正在构建一个应用程序,它将具有类型A的动态分配对象,每个对象都具有动态分配的成员(v),类似于下面的类
class A {
int a;
int b;
int* v;
};
其中:
应用程序可能会有大量此类对象,并且主要需要通过CPU流式传输大量这些对象,但只需要对成员变量执行非常简单的计算。
目标平台是带有x86 / AMD64处理器,Windows或Linux操作系统的标准台式机,并使用GCC或MSVC编译器进行编译。
答案 0 :(得分:2)
如果你有充分的理由关心表现......
动态分配v可能意味着A的实例及其成员v 是不是一起在记忆中?
如果它们都被分配了“新”,那么很可能它们将彼此靠近。但是,当前的记忆状态会极大地影响这种结果,这在很大程度上取决于你对记忆的所作所为。如果你只是一个接一个地分配了这些东西,那么后来几乎肯定会“几乎连续”。
如果A实例在堆栈中,则其“v”极不可能在附近。
如果此类碎片是性能问题,是否有任何可行的技术 允许A和v分配在连续的内存区域吗?
为两者分配空间,然后将新的位置放入该空间。它很脏,但通常应该有效:
char* p = reinterpret_cast<char*>(malloc(sizeof(A) + sizeof(A::v)));
char* v = p + sizeof(A);
A* a = new (p) A(v);
// time passes
a->~A();
free(a);
或者是否有任何技术可以帮助进行内存访问,例如预取方案?
预取是特定于编译器和平台的,但许多编译器都有可用的内在函数。请注意,如果您要立即尝试访问这些数据,那么预测将具有任何价值,您需要在需要数据之前进行数百次循环。也就是说,它可以加速巨大。内在函数看起来像__pf(my_a->v);
如果在编译时可以知道v的大小或可接受的最大大小 将int替换为固定大小的数组,如int v [max_length]导致更好 性能
也许。如果固定大小的缓冲区通常接近你需要的大小,那么它可能会大大提高速度。以这种方式访问一个 A实例总是会更快,但如果缓冲区不必要地巨大且很大程度上未使用,那么您将失去更多对象适合缓存的机会。即最好在缓存中包含更多小的对象,而不是让大量未使用的数据填满缓存。
具体取决于您的设计和性能目标。关于这一点的有趣讨论,在具有特定编译器的特定硬件上存在“真实世界”特定问题,请参阅The Pitfalls of Object Oriented Programming(这是PDF的Google Docs链接,可以找到PDF本身{{ 3}})。
答案 1 :(得分:1)
动态分配v是否可能意味着A及其成员v的实例不在内存中?
是的,很可能。
可以使用哪些工具和技术来测试这种碎片是否是性能瓶颈?
cachegrind,shark。
如果此类碎片是性能问题,是否有任何技术可以允许A和v在连续的内存区域中分配?
是的,您可以将它们分配在一起,但您应该首先看看它是否是一个问题。例如,您可以使用竞技场分配,或者编写自己的分配器。
或者是否有任何技术可以帮助内存访问,例如预取方案?例如,获取类型A的对象在预取v。
的同时对其他成员变量进行操作
是的,你可以这样做。最好的办法是分配彼此靠近的记忆区域。
如果在编译时可以知道v的大小或可接受的最大大小,那么将v替换为固定大小的数组,如int v [max_length]会导致更好的性能吗?
可能会也可能不会。它至少会使v成为结构成员本地。
答案 2 :(得分:0)
如果你需要通过CPU传输大量的这些并且对每一个进行很少的计算,正如你所说,我们为什么要进行所有这些内存分配?
你能拥有一个结构副本和一个(大)缓冲区v
,将你的数据读入它(二进制,速度),做你很少的计算,然后继续下一个。
该程序应该花费近100%的时间在I / O上。 如果在它运行时暂停几次,那么在调用像FileRead这样的系统例程的过程中几乎每次都会看到它。有些剖析器可能会向您提供此信息,但它们往往会对I / O时间过敏。