【发布时间】:2015-02-27 06:54:39
【问题描述】:
我有一个实现两个简单的、预先确定大小的堆栈的类;它们存储为由构造函数预先确定大小的向量类型类的成员。它们是小型且缓存行大小友好的对象。 这两个堆栈的大小是恒定的,持久化和延迟更新,并且经常通过一些计算成本低的方法一起访问,但是这些方法可以调用很多次(每秒数万到数十万次)。
所有对象都已经处于良好状态(代码很干净并且可以执行它应该做的事情),所有大小都得到控制(64k 到 128k 大多数情况下,整个操作链包括结果,很少接近 256k,所以更糟糕的是 L2 查找,通常是 L1)。
一些自动矢量化开始发挥作用,但除此之外,它始终是单线程代码。
这个类,减去一些小东西和填充,看起来像这样:
class Curve{
private:
std::vector<ControlPoint> m_controls;
std::vector<Segment> m_segments;
unsigned int m_cvCount;
unsigned int m_sgCount;
std::vector<unsigned int> m_sgSampleCount;
unsigned int m_maxIter;
unsigned int m_iterSamples;
float m_lengthTolerance;
float m_length;
}
Curve::Curve(){
m_controls = std::vector<ControlPoint>(CONTROL_CAP);
m_segments = std::vector<Segment>( (CONTROL_CAP-3) );
m_cvCount = 0;
m_sgCount = 0;
std::vector<unsigned int> m_sgSampleCount(CONTROL_CAP-3);
m_maxIter = 3;
m_iterSamples = 20;
m_lengthTolerance = 0.001;
m_length = 0.0;
}
Curve::~Curve(){}
请忍受冗长,我正在努力自我教育,并确保我不会用一些半知半解的知识来操作:
鉴于在这些操作上运行的操作及其实际使用情况,性能主要受内存 I/O 限制。 我有几个与数据的最佳定位相关的问题,请记住这是在 Intel CPU(Ivy 和一些 Haswell)和 GCC 4.4 上,我没有其他用例:
我假设如果控件和段的实际存储与 Curve 的实例相邻,那么这是缓存的理想方案(从大小上看,很多可以很容易地适合我的目标 CPU)。 一个相关的假设是,如果向量远离 Curve 的实例,并且在它们之间,当方法交替访问这两个成员的内容时,将会更频繁地驱逐和重新填充 L1 缓存。
1) 是否正确(数据是从在新操作中首次查找的地址中提取的整个缓存大小的数据,而不是在方便的多个适当大小的段中),还是我误解了缓存机制并且缓存可以拉取并保留多个较小的内存段?
2) 按照上述情况,就纯粹的情况而言,我所有的测试总是以类的实例和连续的向量结束,但我认为这只是运气不好,无论在统计上多么有可能。通常实例化该类仅保留该对象的空间,然后将向量分配在下一个可用的空闲连续块中,如果先前在内存中找到一个小的空位,则不能保证它位于我的 Curve 实例附近的任何地方。 这是正确的吗?
3) 假设 1 和 2 是正确的,或者从功能上来说足够接近,我理解为了保证性能,我必须编写一个分配器来确保类对象本身在实例化时足够大,然后复制我自己和那里的向量参考那些。 如果这是解决问题的唯一方法,我可能可以破解类似的方法,但如果有很好/聪明的方法来解决类似的问题,我宁愿不要可怕地破解它。任何关于最佳实践和建议方法的指示都会非常有帮助(除了“不要使用 malloc,它不能保证是连续的”,我已经记下来了 :))。
【问题讨论】:
标签: c++ caching memory vector static-memory-allocation