节点预分配向量中的无锁树节点分配

Posted

技术标签:

【中文标题】节点预分配向量中的无锁树节点分配【英文标题】:Lock-free Tree Node Assignment in Preallocated Vector of Nodes 【发布时间】:2018-07-31 14:54:38 【问题描述】:

我目前正在尝试多线程创建一个,其中保存树的类在一大块中预先分配了std::vectorNodes特定大小(从概念上讲,大小是任意的)。额外的 Node 块仅在必要时创建,这是因为树很快变得非常大,我想避免经常使用 new 运算符以提高时间效率。

Nodes 的这些向量定义为:std::vector< std::vector< Node > > nodes

head 跟踪内部向量中的位置,chunkCount 跟踪当前正在使用的外部向量。

向量在构造函数中被调整为:

nodes.resize( 1 );
nodes[chunkCount].resize( CHUNK_SIZE );

Node 的简化版本是:

typedef struct Node 
    int val;
    Node* subnodes[5];
 Node;

创建一个新的Node 如下:

void TreeClass::createNode( Node* node, short index, int val )

    omp_set_lock( &treeLock ); // treeLock belongs to TreeClass
    head++;
    if( head == CHUNK_SIZE ) 
        std::vector< Node > tempNodeVec( CHUNK_SIZE );
        nodes.push_back( tempNodeVec );
        chunkCount++;
        head = 0;
    
    node->subnodes[index] = &( nodes[chunkCount][head] );
    omp_unset_lock( &treeLock );

    node->subnodes[index]->val = val;

这工作得很好。然而我担心的是,在创建节点时除了一个线程之外的所有线程都被阻塞,这种情况经常发生,所以很多时间都被阻塞或锁定/解锁treeLock,因此我希望使这个函数无锁 strong> 但到目前为止我的尝试都失败了。

更改headchunkCount 很容易,无需使用#pragma omp atomic(或使用std::atomic&lt; int &gt;s)锁定,但这是确保if( ... ) 语句只执行一次和之前的逻辑任何线程都会继续分配孩子的地址,即确保他们使用正确/更新的chunkCounthead

阅读关于无锁算法的一个想法是在Node 中使用std::atomic&lt; Node* &gt; subnodes[5] 并执行CAS 操作,等待正确更新的headchunkCnt 但不知道什么是“正确的”,我怎么知道我在等什么?

另一个(天真的)想法是:

int myHead;
if( ++head == CHUNK_SIZE ) 
    std::vector< Node > tempNodeVec( CHUNK_SIZE );
    nodes.push_back( tempNodeVec );
    chunkCount++;
    myhead = head = 0;
 else 
    myhead = head;
    while( head > CHUNK_SIZE )
        myHead = ++head;

node->subnodes[index] = &( nodes[chunkCount][myHead] );

这个想法是只有一个线程进入if( ... ),直到将head设置为0,其余的将卡在else ... 中,但我已经可以看到与此相关的许多问题接近

对此的任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

我建议您使用线程专用内存池。为此,您可以使用如下注释:

#pragma omp threadprivate(nodes)

这不仅比尝试保护对共享内存池的访问简单得多,而且由于数据局部性,它还可能提高性能。

注意:使用您的解决方案实现基于原子的无锁是不可能的,因为 nodes[chunkCount](每次分配都需要)必须始终受到保护,以免受 nodes.push_back 的影响。

全功能的内存池更复杂,但作为一个小步骤,您可以尝试使用std::deque。它提供了您需要的东西,而不会弄乱两个向量 - 在恒定时间内插入元素,同时不会使现有元素的指针无效。您的控制权较少,但这是一个好的开始。

【讨论】:

感谢您的信息,我会查看线程私有内存。 std::deque 看起来是这个项目的好主意。再次感谢

以上是关于节点预分配向量中的无锁树节点分配的主要内容,如果未能解决你的问题,请参考以下文章

将地址存储到向量中的堆栈分配对象

无法使用服务 api 发布需要预分配值的节点

链表的无锁操作 (JAVA)

DPDK预分配了多少虚拟内存

如何将值向量分配给R中igraph中的顶点标签?

为动态数据结构预分配内存