- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我想将原子uint32s的uint64原子计数器拼凑在一起。计数器只有一个作者和多个读者。 writer是信号处理程序,因此不能阻塞。
我的想法是将具有低位的世代计数用作读取锁定。读取器重试,直到在读取期间生成计数稳定为止,并且低位未设置。
以下代码在设计和使用内存顺序时是否正确?有没有更好的办法?
using namespace std;
class counter {
atomic<uint32_t> lo_{};
atomic<uint32_t> hi_{};
atomic<uint32_t> gen_{};
uint64_t read() const {
auto acquire = memory_order_acquire;
uint32_t lo, hi, gen1, gen2;
do {
gen1 = gen_.load(acquire);
lo = lo_.load(acquire);
hi = hi_.load(acquire);
gen2 = gen_.load(acquire);
} while (gen1 != gen2 || (gen1 & 1));
return (uint64_t(hi) << 32) | lo;
}
void increment() {
auto release = memory_order_release;
gen_.fetch_add(1, release);
uint32_t newlo = 1 + lo_.fetch_add(1, release);
if (newlo == 0) {
hi_.fetch_add(1, release);
}
gen_.fetch_add(1, release);
}
};
auto acquire = memory_order_release;
最佳答案
这是一个已知的模式,称为SeqLock。 https://en.wikipedia.org/wiki/Seqlock。 (为了简化起见,只有一个作者,因此不需要额外的支持来排除同时作者。)
您不需要或不希望计数器变量本身增加使用原子RMW操作。您可以只用原子32位加载来加载两个半块,对其进行递增并原子存储结果。 (使用便宜的relaxed
或release
存储顺序,并使用release
存储进行第二次计数器更新)。
同样,计数器也不必是原子RMW。
编写器仅需要纯加载和纯存储(仅具有发布顺序),这些发布和发行版比原子RMW便宜(很多),或者仅具有seq_cst排序的存储:
release
的两个部分的成本。
value
必须是
atomic<T>
,这使获取编译器生成可能同时加载这两个部分的最有效代码的不便。例如ARM
ldp
/
stp
负载对可能不是原子的,但这无关紧要。 (而且编译器通常不会将两个单独的原子32位负载优化为一个更大的负载。)
volatile uint64_t
和
atomic<uint64_t>
的并集
SeqLock<class T>
模板,但我还没有完成答案(弄清楚哪些ARM版本具有64位原子加载和存储)。
atomic<T>
上的无锁原子操作,以阻止您在毫无意义时使用它。 (通过定义
IGNORE_SIZECHECK
禁用该功能以进行测试。)TODO:透明地回过头去做,可能是使用模板专门化,而不是使用
static_assert
。
inc()
提供了
T
函数,该函数支持
++
运算符。 TODO是一个
apply()
,它接受一个lambda来对
T
做某事,并在序列计数器更新之间存储结果。
// **UNTESTED**
#include <atomic>
#ifdef UNIPROCESSOR
// all readers and writers run on the same core
// ordering instructions at compile time is all that's necessary
#define ATOMIC_FENCE std::atomic_signal_fence
#else
// A reader can be running on another core while writing
// memory barriers or ARMv8 acquire / release loads / store are needed
#define ATOMIC_FENCE std::atomic_thread_fence
#endif
// using fences instead of .store(std::memory_order_release) will stop the compiler
// from taking advantage of a release-store instruction, like on AArch64 or x86
// SINGLE WRITER only.
// uses volatile + barriers for the data itself, like pre-C++11
template <class T>
class SeqLocked
{
#ifndef IGNORE_SIZECHECK
// sizeof(T) > sizeof(unsigned)
static_assert(!std::atomic<T>::is_always_lock_free, "A Seq Lock with a type small enough to be atomic on its own is totally pointless, and we don't have a specialization that replaces it with a straight wrapper for atomic<T>");
#endif
// C++17 doesn't have a good way to express a load that doesn't care about tearing
// without explicitly writing it as multiple small parts and thus gimping the compiler if it can use larger loads
volatile T data; // volatile should be fine on any implementation where pre-C++11 lockless code was possible with volatile,
// even though Data Race UB does apply to volatile variables in ISO C++11 and later.
std::atomic<unsigned> seqcount{0}; // Even means valid, odd means modification in progress.
// unsigned wraps around at a power of 2 on overflow
public:
T get() const {
unsigned c0, c1;
T tmp;
do {
c0 = seqcount.load(std::memory_order_relaxed); // or this can be a std::memory_order_acquire for multicore so AArch64 can use LDAR
ATOMIC_FENCE(std::memory_order_acquire);
tmp = (T)data; // load
ATOMIC_FENCE(std::memory_order_acquire); // LoadLoad barrier
c1 = seqcount.load(std::memory_order_relaxed);
} while(c0&1 || c0 != c1); // retry if the counter changed or is odd
return tmp;
}
// TODO: a version of this that takes a lambda for the operation on tmp
T inc() {
unsigned orig_count = seqcount.load(std::memory_order_relaxed);
seqcount.store(orig_count+1, std::memory_order_relaxed);
ATOMIC_FENCE(std::memory_order_release);
// make sure the data stores appear after the first counter update.
T tmp = data; // load
++tmp;
data = tmp; // store
ATOMIC_FENCE(std::memory_order_release);
seqcount.store(orig_count+2, std::memory_order_relaxed); // Or use mo_release here, better on AArch64
return tmp;
}
void set(T newval) {
unsigned orig_count = seqcount.load(std::memory_order_relaxed);
seqcount.store(orig_count+1, std::memory_order_relaxed);
ATOMIC_FENCE(std::memory_order_release);
// make sure the data stores appear after the first counter update.
data = newval; // store
ATOMIC_FENCE(std::memory_order_release);
seqcount.store(orig_count+2, std::memory_order_relaxed); // Or use mo_release here, better on AArch64
}
};
/***** test callers *******/
#include <stdint.h>
struct sixteenbyte {
//unsigned arr[4];
unsigned long a,b,c,d;
sixteenbyte() = default;
sixteenbyte(const volatile sixteenbyte &old)
: a(old.a), b(old.b), c(old.c), d(old.d) {}
//arr(old.arr) {}
};
void test_inc(SeqLocked<uint64_t> &obj) { obj.inc(); }
sixteenbyte test_get(SeqLocked<sixteenbyte> &obj) { return obj.get(); }
//void test_set(SeqLocked<sixteenbyte> &obj, sixteenbyte val) { obj.set(val); }
uint64_t test_get(SeqLocked<uint64_t> &obj) {
return obj.get();
}
// void atomic_inc_u64_seq_cst(std::atomic<uint64_t> &a) { ++a; }
uint64_t u64_inc_relaxed(std::atomic<uint64_t> &a) {
// same but without dmb barriers
return 1 + a.fetch_add(1, std::memory_order_relaxed);
}
uint64_t u64_load_relaxed(std::atomic<uint64_t> &a) {
// gcc uses LDREXD, not just LDRD?
return a.load(std::memory_order_relaxed);
}
void u64_store_relaxed(std::atomic<uint64_t> &a, uint64_t val) {
// gcc uses a LL/SC retry loop even for a pure store?
a.store(val, std::memory_order_relaxed);
}
volatile
规则,较大的结构类型的复制效率可能较低。
volatile T data
。从技术上讲,这是数据种族的未定义行为,但是我们在实践中使用的所有编译器都可以通过C++ 11之前的多线程访问
volatile
对象。在C++ 11之前的版本中,人们甚至在某些尺寸上都依赖原子性。我们不这样做,我们检查计数器,仅在没有并发写入的情况下才使用读取的值。 (这就是SeqLock的重点。)
volatile T data
的一个问题是,在ISO C++中,除非您从
T foo = data
对象提供了一个复制构造函数,否则
volatile
不会为结构对象编译。
sixteenbyte(const volatile sixteenbyte &old)
: a(old.a), b(old.b), c(old.c), d(old.d) {}
volatile
实际上是错误的工具此处,并且纯
T data
具有足够的防护以确保读取实际上发生在原子计数器的读取之间会更好。例如我们可以使用
asm("":::"memory");
编译器屏障在GNU C中做到这一点,以防止在访问之前/之后进行重新排序。这样一来,编译器就可以使用SIMD vector 或其他任何方式复制较大的对象,而使用单独的
volatile
访问则不会这样做。
std::atomic_thread_fence(mo_acquire)
也将是一个足够的障碍,但我不确定100%。
volatile
聚合(结构),编译器将发出通常复制该字节的asm。但是在C++中,显然我们无法拥有美好的事物。
关于c++ - 用32位原子实现64位原子计数器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54611003/
我在leetcode上看到这段代码,是一道求众数的题,下面是题目描述: 给定一个大小为 n 的数组,找到多数元素。众数元素是出现次数超过 ⌊ n/2 ⌋ 次的元素。 你可以假设数组是非空的并且多数元素
每次在 JavaScript 中执行特定操作时,例如: $(function() { $('#typing').keyup(function () { switch($(this)
我一直在为网页设计一个计数器,但我一直被这个我无法解决的功能所困扰。 我有一个 4 个 div 的计数器,因为其中两个是小数字,另外两个是大数字,所以第一个运行得很快,我看不到它们的功能。 有人知道如
我已经在文档中进行了一些搜索,并在网上花了一段时间,但找不到解决方案!我希望警报告诉我单击 .thumb 时它处于each() 的哪一次迭代。 EG:有六个.thumb,我点击数字3,浏览器弹出3!
在 Handlebars 中,假设我有 names 的集合.我能怎么做 {{#each names}} {{position}} {{name}} {{/each}} 在哪里 {{position}}
这个问题在这里已经有了答案: Numbering rows within groups in a data frame (9 个回答) 4年前关闭。 我们如何在数据帧的每组中生成唯一的 ID 号?以下
我正在努力解决以下问题。我希望为给定的“一”序列创建一个计数器。例如,我有以下内容: 1 1 1 1 0 0 1 1 1 0 0 1 1 1 1 鉴于该序列,我希望为 1 的每个序列设置一个计数器直到
我正在努力解决以下问题。我希望为给定的“一”序列创建一个计数器。例如,我有以下内容: 1 1 1 1 0 0 1 1 1 0 0 1 1 1 1 鉴于该序列,我希望为 1 的每个序列设置一个计数器直到
我有一个jsfiddle here 这是一个简单的 JavaScript 函数,可以计算出设定的数字。 是否可以进行这种计数,但也保留一位小数 所以它算 1.1、1.2、1.3 等。 func
我正在构建一个计数器,当我按下鼠标时,它应该增加到 maxValue 并且减少不超过 0。我还可以选择将计数器重置为其初始值:0。另外,如果 maxValue 是偶数,它应该计数到该数字。但是,如果
所以我成功地为字母和单词构建了其他计数器,但现在我只能用这个来计算句子。我的代码如下,当我运行它时,它会返回很多错误消息: #include #include #include int main
Closed. This question is off-topic。它当前不接受答案。
我需要一个计数器,它会随着某些任务的完成而递增。我们只需要最后一小时的值,即窗口将移动而不是静态时间。 解决此问题的最佳方法是什么?我能想到的一种方法是拥有一个大小为 60 的数组,每分钟一个,并更新
我希望使用计数器来为我提供独特的引用系统。我想单击一个按钮,然后检查一个字段/文件中的最后一个数字,然后简单地向其添加 1,然后将其插入到屏幕上的字段中? 不确定执行此操作的最佳方法或具体如何执行此操
我有一个用 php 制作的表格,在该表格内我显示了数据库中的一些内容。我在每个 td 中创建了一个简单的按钮(类似于 Like),我希望每次点击它都会增加 1。这是带有按钮的行: echo "
如何将数据库中的值转换为可用于 if else 函数的 int 值? 例如:在我的数据库“armnumber = 3”中,如何在 if else 函数中使用它? 代码 string myConnect
我需要生成唯一的“ids”,问题是,它只能在 1 - 99999 之间。 “好”的是,它仅在与另一列组合时必须是唯一的。 我们有组,每个组都有自己的“group_id”,每个组都需要类似 unique
有这个简单的代码: UPDATE counter SET c= c +1 where id = 1; 并且它在开头的 c 字段中为 null 的情况下不起作用。它只有在已经输入了一些数字时才有效,也就
我正在尝试在 python 中构建一个具有闭包属性的计数器。以下工作中的代码: def generate_counter(): CNT = [0] def add_one():
我使用 CSS 来计算 HTML 文档中的部分: body {counter-reset: sect;} section:before { counter-increment: sect;
我是一名优秀的程序员,十分优秀!