Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 一种多线程读、少线程写的高性能排序数据结构的设计_Multithreading_Algorithm_Delphi_Data Structures_Thread Safety - Fatal编程技术网

Multithreading 一种多线程读、少线程写的高性能排序数据结构的设计

Multithreading 一种多线程读、少线程写的高性能排序数据结构的设计,multithreading,algorithm,delphi,data-structures,thread-safety,Multithreading,Algorithm,Delphi,Data Structures,Thread Safety,我有一个有趣的数据结构设计问题,这超出了我目前的专业知识。我正在寻找解决这个问题的数据结构或算法答案 要求: 在一个位置存储合理数量的(指针地址、大小)对(实际上是两个数字;第一个用作排序键) 在高度线程化的应用程序中,许多线程将查找值,以查看特定指针是否在(地址、大小)对中的一个内-也就是说,如果该对定义了内存范围,指针是否在列表中的任何范围内。线程很少在此列表中添加或删除条目 读取或搜索值必须尽可能快,每秒发生数十万到数百万次 添加或删除值(即改变列表)的情况要难得多;表现并不那么重要 列

我有一个有趣的数据结构设计问题,这超出了我目前的专业知识。我正在寻找解决这个问题的数据结构或算法答案

要求:

  • 在一个位置存储合理数量的
    (指针地址、大小)
    对(实际上是两个数字;第一个用作排序键)
  • 在高度线程化的应用程序中,许多线程将查找值,以查看特定指针是否在
    (地址、大小)
    对中的一个内-也就是说,如果该对定义了内存范围,指针是否在列表中的任何范围内。线程很少在此列表中添加或删除条目
  • 读取或搜索值必须尽可能快,每秒发生数十万到数百万次
  • 添加或删除值(即改变列表)的情况要难得多;表现并不那么重要
  • 列表内容过期是可以接受的,但并不理想,即线程的查找代码找不到应该存在的条目,只要该条目在某个点上存在
我非常希望避免一个幼稚的实现,比如使用一个关键部分来序列化对排序列表或树的访问。哪些数据结构或算法可能适合此任务


标记为Delphi,因为我使用该语言 这项任务。语言不可知论的答案非常受欢迎

但是,我可能无法使用任何标准 任何语言的图书馆都不需要太多的关注。原因是内存访问 (对象及其内存的分配、释放等,例如 树节点等)是严格控制的,必须通过我自己的 功能。同一程序中其他地方的当前代码使用 红/黑的树和一点黑,我自己写的。对象 节点分配通过自定义内存分配例程运行。 这超出了问题的范围,但这里提到它是为了避免 像“使用STL结构foo”这样的答案。我喜欢算法或 结构回答,只要我有正确的参考资料或教科书, 我可以实现我自己


我将使用
t字典
(来自
Generics.Collections
)和
TMREWSync
(来自
SysUtils
)进行多读独占写访问
TMREWSync
允许多个读卡器模拟访问词典,只要没有活动的编写器。字典本身提供了指针的O(1)查找

如果您不想使用RTL类,那么答案是:使用一个与多读独占写同步对象相结合的哈希映射


编辑:刚刚意识到您的对实际上代表内存范围,因此哈希映射不起作用。在这种情况下,您可以使用排序列表(按内存地址排序),然后使用二进制搜索快速查找匹配范围。这使得查找O(logn)而不是O(1)。

稍微探索一下复制的想法

从正确性的角度来看,读写器锁将完成这项工作。然而, 在实践中,读者可以同时并行地进行阅读 在访问结构时,它们将在锁上创建一个巨大的争用,因为 很明显,即使对于读访问,锁定也涉及到对锁本身的写入。 这将降低多核系统的性能,甚至会降低多套接字的性能 系统

性能低下的原因是缓存线失效/传输流量 在磁芯/插座之间。(顺便说一下,这是一项非常近期的、非常有趣的研究 关于这个问题)

当然,我们可以通过 每个内核上的结构副本,并将读卡器线程限制为仅访问 本地复制到他们当前正在执行的核心。这需要某种机制使线程获得其当前的核心id。它还依赖于操作系统调度程序,以避免在核心之间无偿移动线程,即在某种程度上保持核心亲和力。 事实上,大多数当前的操作系统都能做到这一点

至于编写者,他们的工作将是更新所有现有副本,方法是获取每个写入锁。一次更新一棵树(显然结构应该是某棵树)确实意味着副本之间存在暂时的不一致性。从这个问题 说明此接缝可接受。当一个作者工作时,它会在一个单独的页面上阻止读者 核心,但不是所有的读者。缺点是,作者有权执行相同的工作 多次-系统中有内核或插槽时的次数

附言

也许,只是也许,另一种选择是类似于某种方法,但我不知道
这是很好的,所以我在提到它之后就停下来:)

通过复制,您可以: -数据结构的一个副本(列表w/二进制搜索,提到的间隔树,…)(例如,“原始”数据结构),仅用于查找(读取访问)。 -第二个副本,即“更新”副本,在更改数据(写访问)时创建。因此,对更新副本进行写入

一旦编写完成,将一些“当前”指针从“原始”更改为“更新”版本。涉及到对“原始”副本的访问计数器,当计数器减回到零读卡器时,此副本可能会被销毁

在伪代码中:

// read:
data = get4Read();
... do the lookup
release4Read(data);

// write
data = get4Write();
... alter the data
release4Write(data);


// implementation:            
// current is the datat structure + a 'readers' counter, initially set to '0'
get4Read() {
  lock(current_lock) {              // exclusive access to current
    current.readers++;              // one more reader
    return current;
  }
}

release4Read(copy) {
  lock(current_lock) {              // exclusive access to current
   if(0 == --copy.readers) {        // last reader
     if(copy != current) {          // it was the old, "original" one
       delete(copy);                // destroy it
     }
   }
  }
}

get4Write() {

   aquire_writelock(update_lock);  // blocks concurrent writers!

   var copy_from = get4Read(); 
   var copy_to = deep_copy(copy_from);
   copy_to.readers = 0;

   return copy_to;
}    

release4Write(data) {

   lock(current_lock) {              // exclusive access to current
     var copy_from = current;
     current = data; 
   }

   release4Read(copy_from);

   release_writelock(update_lock);  // next write can come
}
要完成有关要使用的实际数据结构的回答,请执行以下操作: 考虑到数据项的固定大小(两个整数元组),也非常小,我将使用数组进行存储,并使用二进制搜索进行查找。(另一种选择是评论中提到的平衡树)

谈论性能:据我所知,“地址”和“大小”定义了范围。因此,查找f
Reader                      Shared data               Writer
======                      ===========               ======
                             current = A:0            

data = get4Read()
   var copy = A:0
   copy.readers++;
                             current = A:1
   return A:1
data = A:1
... do the lookup
release4Read(copy == A:1):
    --copy.readers           current = A:0
   0 == copy.readers -> true

                                                      data = get4Write():
                                                           aquire_writelock(update_lock)
                                                           var copy_from = get4Read():
                                                                  var copy = A:0
                                                                  copy.readers++; 
                             current = A:1
                                                                  return A:1
                                                           copy_from == A:1
                                                           var copy_to = deep_copy(A:1);
                                                           copy_to == B:1
                                                           return B:1
                                                      data == B:1
                                                      ... alter the data
                                                      release4Write(data = B:1)
                                                           var copy_from = current;
                                                           copy_form == A:1
                                                           current = B:1
                             current = B:1 
     A:1 != B:1 -> true
     delete A:1
                                                           !!! release4Read(A:1) !!!