将多个值打包到一个原子中的标准化方法

问题描述 投票:0回答:2

假设我有两个 int32 类型的原子变量,我可以选择将它们表示为

std::atomic<int64> both
并为我的第一个 in 保留前 32 位,为我的第二个 int 保留最后一个。

这在 x64 架构上看起来相当节省空间和时间,更不用说它允许各种黑魔法,因为人们可以抽象各种操作并使它们原子化:

first == a && second ==b

成为

both == ( int64(a) + int64(b) << 32 )
//Or some such... I'm not 100% sure this is correct but you get the idea

我发现这个技巧的一个问题是,我不太擅长在位级别进行操作,而 C++ 在位级别操作方面并不是很友好,特别是当你尝试完成更复杂的操作或将两个以上的变量(例如两个数字和几个布尔值)打包到同一个原子中。

所以我想知道是否有一个标准化的方法来应用这种技巧。一种模式甚至标准功能,在看到时很容易被其他编码人员识别并且更易于实现者使用?同样,这种模式是否足够有用以保证这种标准化,或者与它可能带来的可能的烦恼和 UB 相比,它的用处是否很快就变得过时了?

c++ multithreading bit-manipulation atomic c++17
2个回答
0
投票

使用原子来解决“先读后写”的方法是使用循环:

void setBit(atomic<int64_t>& bitset, int bit)
{
  int64_t val = 1LL << bit;
  int64_t prev = bitset;
  while ((!(bitset & val)) && 
         !bitset.compare_exchange_weak(prev, (prev | val))
    ;
}

您可以扩展此方法来创建通用的按位运算函数


0
投票

如果您不想处理移位和位掩码操作,您可以将值转换为具有适当大小的指针,并使用简单的指针算术或数组索引表示法来访问/存储您需要的值

现代 C++ 爱好者会对此犹豫不决,但以下演示了这两种方法是等效的

在此示例中,我将 4 x int8 值打包到一个 int32 中

int32_t packed_values = 0x01020304;  // packed values [4, 3, 2, 1]

// pointer cast + array dereference approach
int8_t *unpacked_values = (int8_t *)&packed_values;

printf("packed value 0 == %d", unpacked_values[0]);
printf("packed value 1 == %d", unpacked_values[1]);
printf("packed value 2 == %d", unpacked_values[2]);
printf("packed value 3 == %d", unpacked_values[3]);

// shift mask approach
int8_t value0 = (packed_values & 0x000000FF);
int8_t value1 = (packed_values & 0x0000FF00) >> 8;
int8_t value2 = (packed_values & 0x00FF0000) >> 16;
int8_t value3 = (packed_values & 0xFF000000) >> 24;

printf("shift/mast value 0 == %d", value0);
printf("shift/mast value 1 == %d", value1);
printf("shift/mast value 2 == %d", value2);
printf("shift/mast value 3 == %d", value3);

assert (value0 == unpacked_values[0]);
assert (value1 == unpacked_values[1]);
assert (value2 == unpacked_values[2]);
assert (value3 == unpacked_values[3]);

如果您的目标架构不同,请注意字节序

© www.soinside.com 2019 - 2024. All rights reserved.