Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.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
C++ Facebook folly::AccessSpreader是如何工作的?_C++_Multithreading_Atomic_Folly - Fatal编程技术网

C++ Facebook folly::AccessSpreader是如何工作的?

C++ Facebook folly::AccessSpreader是如何工作的?,c++,multithreading,atomic,folly,C++,Multithreading,Atomic,Folly,以下是Facebook Folly库中AccessSpreader的代码: ///AccessSpreader以这种方式安排对条带化数据结构的访问 ///并发执行线程可能访问的方式 ///不同的条纹。它不能保证无竞争访问。 ///您的底层算法必须是线程安全的,不会传播,这是 ///这仅仅是一种优化。AccessSpreader::current(n)通常为 ///比缓存丢失快得多(在我的开发设备上是12纳米,测试速度很快 ///在2.6和3.2内核中)。 /// ///如果可用(且不使用确定性

以下是Facebook Folly库中AccessSpreader的代码:

///AccessSpreader以这种方式安排对条带化数据结构的访问
///并发执行线程可能访问的方式
///不同的条纹。它不能保证无竞争访问。
///您的底层算法必须是线程安全的,不会传播,这是
///这仅仅是一种优化。AccessSpreader::current(n)通常为
///比缓存丢失快得多(在我的开发设备上是12纳米,测试速度很快
///在2.6和3.2内核中)。
///
///如果可用(且不使用确定性测试实现)
///AccessSpreader通过VDSO和
///CacheLocation从sysfs检索的精确位置信息。
///这提供了最佳的反共享,成本仅为
///缓存未命中。
///
///当条纹没有处理器那么多时,我们会尝试以最佳方式
///放置缓存共享边界。这意味着如果你有两个
///条纹和运行在双插座系统上,您的2条条纹将得到每个
///所有的核心都来自一个插座。如果你有16条条纹
///16核系统加上超线程(32个CPU),每个核心将获得
///如果您拥有自己的条带,则根本不会有缓存共享。
///
///AccessSpreader有一个回退机制,用于当无法使用_vdso_getcpu时
///已加载,或在确定性测试期间使用。使用sched_getcpu
///或者getcpu系统调用将否定
///访问扩展,所以我们使用一个线程本地值和一个共享原子
///柜台将通道分散。在缺少快速getcpu()的系统上
///和TLS,我们散列线程id以分散访问。
///
///AccessSpreader以所使用的模板类型为模板
///实现原子,作为实例化底层
///针对生产使用和确定性单元的不同启发法
///测试。有关更多信息,请参阅确定性调度器。如果您没有使用
///DeterministicScheduler,您可以只使用默认模板参数
///一直如此。
模板
结构访问扩展程序{
///返回与当前CPU关联的条带。返回的
///值将小于numStripes。
静态大小\u t电流(大小\u t numstrips){
//WidthAndCputStripe[0]实际上可以正常工作(全部为零),但是
//打电话的人有点不对劲
断言(numStripes>0);
无符号cpu;
getcpuFunc(&cpu,nullptr,nullptr);
返回宽度和CputStripe[std::min(大小(kmaxpus),numStripes)]
[cpu%kmaxpus];
}
私人:
///如果CPU的数量超过这个数量,没有什么会崩溃,但是
///可能是不必要的共享
枚举{kmaxpus=128};
typedef uint8_t CompactStripe;
静态断言(
(kmaxpus和(kmaxpus-1))==0,
“kMaxCpus应该是二的幂,所以模运算很快”);
静态断言(

kmaxpus-1不,这个类并没有做你认为的事情

总体思路是,当您有许多等效的资源/数据结构,并且希望不同的线程访问不同的实例以最小化争用并最大化数据位置时,您可以使用
AccessSpreader
来建议当前核心/线程使用的最佳资源/数据

例如,请参阅。内存池的这种实现维护了许多空闲对象列表,以减少分配/解除分配时的线程争用。下面是如何使用
AccessSpreader

AtomicStruct<TaggedPtr,Atom>& localHead() {
  auto stripe = AccessSpreader<Atom>::current(NumLocalLists);
  return local_[stripe].head;
}
AtomicStruct&localHead(){
自动条带=AccessSpreader::current(NumLocalLists);
返回本地[stripe].head;
}
i、 它给出了一个元素的索引(在一些数组或向量等中),建议当前线程使用该元素

更新(回应评论):不可能总是将不同的索引分配给不同的线程-例如,如果可能的索引(条带)的数量小于CPU的数量;并且评论明确指出“它不保证无争用访问”。该类不仅可用于最小化争用,还可用于最大化数据局部性;例如,您可能希望在具有公共缓存的线程之间共享某些数据实例。因此,建议的索引是两个变量的函数:当前CPU(使用
getCpuFunc
在内部获得)和条带数(作为参数
numStripes
)传递)-这就是需要2D数组的原因。在程序初始化时使用系统特定信息(通过class
cacheLocation
)填充数组,以便建议的索引考虑数据局部性


至于
std::atomic
,它仅用于具有单独的
AccessSpreader
实例化,用于测试和生产使用,如类声明前的注释所述。该类没有(也不需要)任何原子成员变量。

好的,还有一件事-在AccessSpreader::current中,它不能简化为仅使用1D数组作为WidthandCputStripe,或者更好-如果它们只返回CPU号,它实际上会确保不同的内核访问不同的索引?我不太理解它们用于填充width的公式CPutostripe和它试图达到的目标我更新了答案来解释。不过我没有深入解释公式的细节,只是试图解释其意图。
AtomicStruct<TaggedPtr,Atom>& localHead() {
  auto stripe = AccessSpreader<Atom>::current(NumLocalLists);
  return local_[stripe].head;
}