随时可用的原子位设置和位重置函数?

vir*_*ino 3 c linux bit-manipulation x86-64 atomic

我正在尝试在 Linux 下用 C 语言编写我的第一个多线程程序。我已经有一个程序在大缓冲区中设置和重置位,现在我只想让它更快 - 尽可能快,而不用在汇编程序中编写所有内容。

对于单线程程序,我为位操作定义了自己的宏(它们看起来又大又丑,但它们有效):

#define CELL_SIZE (64)

#define getBit(bitIdx, arr)   ( arr[ (bitIdx) / CELL_SIZE ] &   ( (1uL << (CELL_SIZE - 1)) >> (bitIdx) % CELL_SIZE))
#define resetBit(bitIdx, arr) ( arr[ (bitIdx) / CELL_SIZE ] &= ~( (1uL << (CELL_SIZE - 1)) >> (bitIdx) % CELL_SIZE))
Run Code Online (Sandbox Code Playgroud)

显然,这些操作绝非原子操作。

我做了一些研究,找到了几个答案。一些答案是关于如何在汇编程序中编写我自己的函数(我想避免)。

其他网站(这个这个)似乎准确地谈论了我所需要的 - 除了我不知道如何使用这些接口/宏。

我尝试了以下包含,但它们不起作用。

#include <linux/bitmap.h>
#include <asm/bitops.h>
Run Code Online (Sandbox Code Playgroud)

所以,基本上,我问如何使用已经实施的内容来完成我的工作?我应该包含哪些标头?

我不介意接口是否不在内核中,只要工作完成即可。

注意:每个线程都会一次(重新)设置一位——我不打算做任何 VOODOO 复杂的事情。


一个附带问题是:哪一个更快(总体而言,用伪代码编写):

reset_bit();
Run Code Online (Sandbox Code Playgroud)

或者

if ( bit_is_set() )
    reset_bit();
Run Code Online (Sandbox Code Playgroud)

通常情况下,reset_bit() 操作是不必要的,因为该位已经复位。关于设置设置位的同样问题。

Nat*_*dge 5

为了直接回答这个问题,最直接、最容易获得的解决方案是 C11 <stdatomic.h>,任何用于多处理器系统的现代 C 编译器都应该提供它。

可用操作包括原子 OR/AND,可用于设置和清除位。所以你可能会这样做

#include <stdatomic.h>
#include <stdint.h>
#include <stddef.h>

typedef uint64_t cell_t;

#define CELL_SIZE (64)

static inline _Atomic cell_t *get_cell(size_t bitIdx, _Atomic cell_t *arr) {
    return arr + (bitIdx / CELL_SIZE);
}

static inline cell_t get_mask(size_t bitIdx) {
    return (1uL << (CELL_SIZE - 1)) >> (bitIdx) % CELL_SIZE;
}
  
static inline cell_t getBit(size_t bitIdx, _Atomic cell_t *arr) {
    return atomic_load(get_cell(bitIdx, arr)) & get_mask(bitIdx);
}

static inline void resetBit(size_t bitIdx, _Atomic cell_t *arr) {
    atomic_fetch_and(get_cell(bitIdx, arr), ~get_mask(bitIdx));
}

static inline void setBit(size_t bitIdx, _Atomic cell_t *arr) {
    atomic_fetch_or(get_cell(bitIdx, arr), get_mask(bitIdx));
}
Run Code Online (Sandbox Code Playgroud)

尝试一下神箭

我冒昧地用内联函数替换了您的宏,这在几乎所有情况下都是更好的选择。

*_explicit在某些平台上,通过使用带有 的版本,您也许可以在一定程度上加快速度memory_order_relaxed,但这在很大程度上取决于函数的使用方式,并且内存排序的介绍超出了本文的范围。

对性能的影响更加困难。通常,原子读-修改-写操作比非原子慢很多。如果 CPU 之间存在缓存线争用,速度会进一步减慢。除非您能够很好地控制这些方面,否则您计划使用更多核心来完成任务可能会使其速度变慢。

至于你最后一个关于是否先测试位的问题,很难说。加载通常比读取-修改-写入更快。然而,进行测试和条件分支的成本,尤其是错误预测该分支的可能性,可能会抵消节省的成本。