神秘指针相关的多线程减速

时间:2009-11-06 18:41:30

标签: c++ performance multithreading optimization pointers

背景:所以我正在研究一个光线跟踪器...为了构建空间分区方案,我最初有一些像这样的代码:

if (msize <= 2) { // create a leaf node
    Model **models = new Model*[msize];
    for (uint i=0; i<msize; ++i)
        models[i] = &mlist[i];
    *arrayPtr = Node(models, msize); // class Node contains a copy of models
    ... increment arrayPtr ...
    return;
}

基本上,在构造这个空间分区树之后,光线遍历树寻找模型,模型全部存储在一个大数组中。叶子节点包含指向模型指针的指针。

然后我意识到嘿,我没有理由加上额外的间接水平;如果我正确安排我的模型,我可以让叶子节点直接指向大型模型。大数组中彼此相邻的模型都属于给定的叶节点,因此叶子将包含指向模型的指针。所以我做了这个,并用其他一切保持不变来测试它。

现在人们会认为这显然会加快计划的进度。好吧,它确实加速了单线程版本(大约10%),但它减慢了多线程版本(大约15%!如果你正在做大量优化,这是非常重要的。)我很喜欢关于如何解决这个问题的失误 - 我认为间接是坏的,我认为减少内存使用是好的,特别是用于多线程...叶子节点或模型没有任何写入,所有写入都完成了单独的数据结构。

关于如何分析问题的任何指示/建议都会很棒。

一些杂项统计:cachegrind告诉我双间接方法的指令引用/缓存未命中次数较少,但更多的数据引用/缓存未命中。但两者的区别并不大。

编辑:根据要求,我关注的数据结构:

class Node {
    ushort type;
    union {
        ushort axisID;
        ushort childrenSize;
    };
    union {
        Model **models;
        Node *rightChild;
    };
    float leftPlane, rightPlane;
    ... public methods and stuff ...
}

我基本上将Model **models更改为Model *models,然后我获得速度下降。类Model本身包含指向两个抽象类的指针ShapeMaterial。这里提到的所有类都是块分配的,Material除外,因为目前我只使用一个。

3 个答案:

答案 0 :(得分:1)

我的第一个猜测是你遇到false-sharing。如果您有多个线程同时修改同一缓存行中的内存,则硬件将花费大量时间在处理器之间传递缓存行的所有权。

答案 1 :(得分:1)

另一个人质疑减速是否来自增加的间接性,或者是你如何分配struct Model的变化。因为您现在将Model结构分配为连续的内存区域,所以相邻结构可能共享相同的缓存行。如果您的线程同时访问相邻的结构,则会争用访问权限。一个读取访问将在一个总线周期停止,同时等待另一个。

什么是sizeof(class Model)?您可以尝试使用虚拟变量对其进行扩展,直到该类为高速缓存行的大小。

另一种可能性是您已更改正在访问的成员变量的对齐方式。如果您的sizeof(class Model)不是机器字长的倍数(例如,8字节),则此类对象的数组将使某些成员与字大小对齐,而某些成员则不对应。未对齐会导致内存总线上的双重读取,因为获取单元从对齐的内存位置读取机器字,并将这两个读取中的寻址值合成。

答案 2 :(得分:0)

我要寻找的最重要的事情是一些不正确的初始化,要么制作重复的数据,要么有不正确的共享数据。这在代码中并不明显,但从**到*时,这是一个明显的错误。