简体   繁体   English

部分更新复杂的C11原子类型和非原子读取优化

[英]Partial updates to complex C11 atomic types and non-atomic read optimizations

I think the easy way to describe my question is to demonstrate it in code, so here is a contrived example in C to highlight the issues I am interested in answering: 我认为描述我的问题的简单方法是在代码中进行演示,因此这是C中人为设计的示例,以突出我有兴趣回答的问题:

// Just some complex user defined type
typedef struct {
    ...
} state_t;

typedef struct {
    state_t states[16];
} state_list_t;

static _Atomic state_list_t s_stateList;
// For non-atomic reads
static state_t * const s_pCurrent = &s_stateList.states[0];

// Called from external threads
void get_state(state_list_t * pStateList)
{
    *pStateList = atomic_load(&s_stateList);
}

// Only called by 'this' thread
static void update_state(struct state_data_t const * pData)
{
    state_list_t stateList = atomic_load(&s_stateList);
    for (int i = 0; i < 16; i++)
    {
        // Do some updating on the data
        do_transition(&stateList[i], pData);
    }
    atomic_store(&s_stateList, stateList);
}

// Only called by 'this' thread
static void apply_state(state_t const * pState)
{
    atomic_store(&s_stateList[0], *pState);
}

// Only called by this thread
static bool check_state()
{
    // Check (read) some values in the current state
    return isOkay(s_pCurrent);
}

First, my apologies for any syntax errors, but this should get the point across... 首先,对于任何语法错误,我深表歉意,但这应该可以理解...

The first two functions are pretty straight forward usage of the C11 atomics, namely one thread is reading a value that another one is writing. 前两个函数非常简单地使用了C11原子,即一个线程正在读取另一个线程正在写入的值。 My specific questions are really regarding the last two functions, apply_state, and check_state, and it really just boils down to whether these are okay things to do. 我的具体问题实际上是关于最后两个功能apply_state和check_state的,实际上只是归结为这些功能是否可行。

In apply_state, you can see that it is only updating part of the structure atomically, specifically, the first element of an array. 在apply_state中,您可以看到它只是原子地更新结构的一部分,特别是数组的第一个元素。 It is my understanding that essentially every element of the _Atomic s_stateList is considered atomic (much like volatile), so the compiler is fine with the atomic_store call, but can this happen while another thread is 'atomically' reading from the object (ie in get_state), or is the synchronization essentially equivalent to a locking / unlock the same mutex in each call? 据我了解,基本上_Atomic s_stateList的每个元素都被视为原子的(非常类似于volatile),因此编译器可以通过atomic_store调用正常运行,但是当另一个线程“以原子方式”从对象读取时(即在get_state中),会发生这种情况吗? ),还是同步实质上等效于在每个调用中锁定/解锁相同的互斥锁? I could see how it is possible that since it is basically a different variable (well, okay same address, but what if I used states[1]?) it could result in a different mutex being used. 我可以看到,由于它基本上是一个不同的变量(嗯,可以使用相同的地址,但是如果使用states [1]怎么办?),则有可能导致使用不同的互斥量。 Also, what happens if state_t happens to be lock free? 另外,如果state_t碰巧是无锁的,会发生什么?

I'm more confident that the check_state function is an okay thing to do here, because it only performs a read on an object that is modified only by the same thread, but I'm wondering if I'm missing anything here. 我更相信check_state函数在这里是可以做的,因为它仅对仅由同一线程修改的对象执行读取,但是我想知道我是否在这里缺少任何内容。 I've just recently discovered that accessing an atomic variable directly (I think via assignment or function argument) is treated exactly like a call to atomic_load() or atomic_store(), so I am wondering if keeping a private reference for non-atomic reads is a worthwhile optimization, or if the compiler is otherwise smart enough to accomplish similar optimization on its own. 我最近发现,直接访问原子变量(我认为是通过赋值或函数参数)就像对待对atomic_load()或atomic_store()的调用一样,所以我想知道是否为非原子读取保留私有引用是值得进行的优化,或者如果编译器足够聪明,可以自己完成类似的优化。

Edit: The result is undefined when dereferencing a non-atomic pointer to an atomic value. 编辑:将非原子指针解引用到原子值时结果不确定。

No this doesn't fit into C11's model for atomics, and for good reasons. 不,这出于原子性原因不适合C11的模型。 _Atomic is only syntactically a qualifier, semantically an _Atomic is a new type. _Atomic在语法上仅是一个限定符,在语义上, _Atomic是一种新类型。 This is reflected by the fact that the standard allows that size and alignement of such types are different from those for the base. 这反映在以下事实上:该标准允许此类类型的大小和对齐方式与基础类型不同。

In your case of a wide atomic type, a permitted implementation of the atomic type is to add a hidden field to the struct that serves as a lock. 在您的广泛原子类型的情况下,原子类型的允许的实现是一个隐藏字段添加到struct充当锁。 Generally, such types are implemented as "not lock-free" that is with some hidden state (within the struct or seperately) that controls access to the data. 通常,此类类型被实现为“非无锁”状态,该状态具有控制数据访问的某种隐藏状态(在struct或在struct内)。

The standard can only guarantee you racefreeness by stitching together an access model. 该标准只能通过将访问模型组合在一起来保证您的竞赛自由。 If you ask your whole data to be accessible atomic (in the sense of indivisible operations on that whole data at once), the model only allows you exactly that. 如果您要求整个数据是原子可访问的(就该一次对整个数据而言不可分割的操作而言),则该模型仅允许您这样做。

Accessing individual fields of an atomic object has undefined behavior. 访问原子对象的各个字段具有未定义的行为。 That means if your platform had specific properties it could allow you access to individual fields. 这意味着,如果您的平台具有特定的属性,则可以允许您访问各个字段。 You'd have to read up your platform's documentation and hope for the best, in particular that they don't change things from one version (compiler, processor, ...) to another. 您必须阅读平台的文档,并希望最好,尤其是不要将内容从一种版本(编译器,处理器等)更改为另一种版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM