缓存行填充对于变量大小为缓存行大小的倍数

5

我正在创建一个非常快速的多线程离散事件模拟框架,该框架的核心使用原子和无锁编程技术,在许多线程之间实现非常快速的执行。这要求我将一些变量与缓存行对齐,并填充剩余的缓存行空间,以避免缓存行竞争。以下是我的做法:

// compute cache line padding size
constexpr u64 CLPAD(u64 _objSize) {
  return ((_objSize / CACHELINE_SIZE) * CACHELINE_SIZE) +
      (((_objSize % CACHELINE_SIZE) > 0) * CACHELINE_SIZE) -
      _objSize;
}

alignas(CACHELINE_SIZE) MyObject myObj;
char padding[CLPAD(sizeof(myObj))];

这对我来说很有效,但今天我在使用此方法处理新对象类型时遇到了一个问题。CLPAD()函数返回填充输入类型到下一个缓存行所需的字符数。然而,如果我输入的类型恰好是缓存行数量的倍数,则CLPAD返回0。如果您尝试创建大小为零的数组,则会出现以下警告/错误:
ISO C++ forbids zero-size array 'padding'

我知道我可以修改CLPAD()来在这种情况下返回CACHELINE_SIZE,但是这样做会浪费一个缓存行的空间。
如果CLPAD返回0,我怎样才能使“padding”的声明消失?
1个回答

4

借鉴自std::aligned_storage<>,我提出了以下内容:

template<class T, bool = false>
struct padded
{
    using type = struct
    {
        alignas(CACHELINE_SIZE)T myObj;
        char padding[CLPAD(sizeof(T))];
    };
};

template<class T>
struct padded<T, true>
{
    using type = struct
    {
        alignas(CACHELINE_SIZE)T myObj;
    };
};

template<class T>
using padded_t = typename padded<T, (sizeof(T) % CACHELINE_SIZE == 0)>::type;

使用方法:

struct alignas(32) my_type_1 { char c[32]; }; // char c[32] to silence MSVC warning
struct my_type_2 { char c[CACHELINE_SIZE * 2]; }; // ditto

int main()
{
    padded_t<my_type_1> pt0;
    padded_t<my_type_2> pt1;

    sizeof(pt0);    // 128
    alignof(pt0);   // 128

    sizeof(pt1);    // 256
    alignof(pt1);   // 128
}

您可以按照自己的方式提供访问myObj的函数。

有使用嵌套结构的理由吗?您可以只使用padded而不需要嵌套的type(除非这是一些元编程技巧)。 - Andrey Turkin
@AndreyTurkin 实际上,您可以不使用嵌套类型。但是,padded<> 的类型包含模板参数,而类型别名提供了一个匿名结构,该结构没有模板参数。 - user2296177
那么这样做的好处是什么?类型数量变少了吗?符号表变小了吗?对象大小变小了吗? - Andrey Turkin
“alignof(T) % CACHELINE_SIZE” 应该改为 “sizeof(T) % CACHELINE_SIZE” 吗? - nic
@nic 是的,确实!因为这正是你实际描述的问题。很高兴你注意到了我的疏忽。这就是我将大小设置为缓存行的倍数的原因。 - user2296177
@AndreyTurkin 我没有足够的经验来回答你所有的问题。我相信这样做会更加清晰,类似于标准的做法(因为我基于对齐存储的设计)。对象大小不会变小/变大。我不喜欢携带我不需要的类型信息的想法,但我不确定是否有其他优点。 - user2296177

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接