我是一个修补匠,对此毫无疑问。基于这个原因(除此之外,几乎没有),我最近做了一些实验来证实我的怀疑,即对struct的写入不是原子操作,这意味着试图强制执行的所谓“不可变”值类型假设某些约束可能无法达到其目标。

我以以下类型为例编写了a blog post about this

struct SolidStruct
{
    public SolidStruct(int value)
    {
        X = Y = Z = value;
    }

    public readonly int X;
    public readonly int Y;
    public readonly int Z;
}


尽管上面的内容看起来像是X != YY != Z的类型,但实际上,如果值是“中间分配”,同时又将其复制到另一个位置,则会发生这种情况线。

好,大不了好奇心和更多。但是后来我有了这样的预感:我的64位CPU实际上应该能够自动复制64位,对吗?那么,如果我摆脱了Z而只停留在XY上怎么办?只有64位;应该可以一步一步覆盖它们。

果然,它奏效了。 (我知道你们中的一些人可能现在正在皱眉,想,是的,这很有趣吗?幽默我。)当然,我不知道这是否可以保证我的系统。我几乎不了解寄存器,高速缓存未命中等等。(我只是在反覆我所听到的术语而没有理解它们的含义);所以目前这对我来说都是个黑匣子。

我尝试的下一个步骤(还是一次预感)是一个结构,该结构由32位使用2个short字段组成。这似乎也表现出“原子可分配性”。但后来我尝试使用3个byte字段尝试使用24位结构:不行。

突然,该结构似乎再次受到“中间分配”副本的影响。

减少到16位,带有2个byte字段:又是原子的!

有人可以向我解释为什么吗?我听说过“位打包”,“高速缓存行跨越”,“对齐”等,但是再次,我真的不知道这意味着什么,也不知道在这里是否相关。但是我感觉好像看到了一种模式,而无法确切地说出它是什么。清晰度将不胜感激。

最佳答案

您要查找的模式是CPU的本机字大小。

从历史上看,x86系列在本机上使用16位值(在此之前是8位值)。因此,您的CPU可以原子地处理这些:设置这些值是一条指令。

随着时间的流逝,本机元素的大小增加到32位,后来又增加到64位。在每种情况下,都添加了一条指令来处理此特定数量的位。但是,为了向后兼容,仍然保留了旧的指令,因此您的64位处理器可以使用所有以前的本机大小。

由于您的struct元素存储在连续的内存中(没有填充,即没有空白空间),因此运行时可以利用此知识仅对这些大小的元素执行该单个指令。简而言之,这会产生您所看到的效果,因为CPU一次只能执行一条指令(尽管我不确定在多核系统上是否可以保证真正的原子性)。

但是,本机元素大小永远不会为24位。因此,没有一条指令可以写入24位,因此需要多条指令,您会失去原子性。

10-04 18:50