【问题标题】:gcc atomic builtins and availability versiongcc atomic builtins 和可用性版本
【发布时间】:2016-04-07 08:36:16
【问题描述】:

我正在尝试验证我是否可以在我拥有的 gcc 编译器(版本 4.3.2)中使用“__atomic_exchange_n”,如果它支持原子,我找不到任何地方,如果它不可以使用“__sync_lock_test_and_set” ?

我在哪里可以看到我的编译器是否支持这些函数,或者它们何时在 gcc 中引入?谢谢!

伪代码:

#if defined HAVE_GCC_SYNC_BUILTINS

     #define AtomicExchange(vP, v)   (void)__sync_lock_test_and_set(vP, v)  

 #elif defined HAVE_GCC_ATOMIC_BUILTINS

     #define AtomicExchange(vP, v)   (void)__atomic_exchange_n(vP,v, o)

 #endif

编辑:

我有 gcc 4.3.2,我无法更新它。 (平台限制) 有没有办法实现 64 位变量的原子存储?

我尝试使用上面所说的“__sync_lock_test_and_set(vP, v)”,对于 int、short 和 char 它有效,但对于 int64(long long),我可以执行存储,因为它给出了“未定义的引用到 __sync_lock_test_and_set_8"。 “_8”是 8 字节(64 位)。

我可以做些什么来实现 64 位原子存储? 谢谢

【问题讨论】:

  • 预处理器指令前的前导下划线是什么?我假设它们在真实代码中不存在?
  • 只是为了识别代码*
  • 能说说CPU架构吗?来自/proc/cpuinfo 的vendor_id 和model name,或uname -m -p,或者您在交叉编译到该架构时使用的设置?某些架构(如 ARM 变体)具有不同的比较和交换/测试和设置操作方法。尽管所有内置插件都可以工作,但其中一些工作得更好(也就是说,速度更快,内部循环的迭代次数更少,等等)。如果我了解架构,我可以展示一些示例案例。
  • @NominalAnimal cpu 架构各不相同,但通常它是 32 位的 powerpc,但 gcc 4.3.2 表示:“未定义对 __sync_lock_test_and_set_8 的引用”用于 64 位变量。谢谢。

标签: c gcc


【解决方案1】:

原子操作有两种基本方法CAS(或比较和交换)和LL/SC(或加载链接/存储条件)。它们的低级差异使得它们不能直接暴露给用户空间。旧式 __sync 内置函数基于使用 CAS 方法的 Intel Itanium 架构;毫不奇怪,它与 LL/SC 风格的架构的原子不是一个非常好的匹配。实际结果是不同的 C 方法(如果必须使用内置同步)在不同的架构上效果最好;新型 atomic 内置函数倾向于在所有架构上产生更好的代码,基于并且更多地接触不同类型的架构。

GCC 4.3.2 仅支持旧式 __sync_ 内置插件。替换__atomic_exchange_n() 操作有两种通用方法:

static inline TYPE atomic_exchange(TYPE *const ptr, const TYPE newval)
{
    TYPE oldval;
    do {
        oldval = *ptr;
    } while (!__sync_bool_compare_and_swap(ptr, oldval, newval));
    return oldval;
}

static inline TYPE atomic_exchange(TYPE *const ptr, const TYPE newval)
{
    TYPE oldval, tmpval;
    oldval = *ptr;
    do {
        tmpval = oldval;
        oldval = __sync_val_compare_and_swap(ptr, tmpval, newval);
    } while (oldval != tmpval);
    return oldval;
}

如果newval 为零或全一,我们也可以使用

static inline TYPE atomic_clear(TYPE *const ptr)
{
    return __sync_fetch_and_and(ptr, (TYPE)0);
}

static inline TYPE atomic_setall(TYPE *const ptr)
{
    return __sync_fetch_and_or(ptr, ~(TYPE)0);
}

如果只有几个处理器/缓存架构目标,您总是可以编写一个行之有效的内联汇编函数,假设硬件实际上支持 64 位原子操作。这很容易,如果你能编译一个简单的新型原子交换函数,比如

TYPE atomic_exchange_prototype(TYPE *const ptr, const TYPE newval)
{
    return __atomic_exchange_n(ptr, newval, __ATOMIC_SEQ_CST);
}

为每个目标架构汇编代码(例如,使用-O2 -S gcc 标志,加上特定于架构的选项;或-O2 --static -c,如果使用 GCC 提供的函数,在这种情况下,objdump -d 可以提供反汇编) .在某些情况下,架构手册实际上描述了如何最好地构建原子操作。

如果硬件架构不支持 64 位原子操作(尽管如果 32 位架构根本不支持 64 位原子操作,我会有点惊讶,即使通过仿真也是如此),可能仍然有解决方法.例如,使用生成计数器。 (基本上,两个或更多原子增量(小)计数器字段用于确定数据何时有效。具体实现取决于需求,尤其是何时(从哪个上下文)修改数据。)

【讨论】:

    【解决方案2】:

    你可以在谷歌上搜索 GCC 4.3.2 规范

    https://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Atomic-Builtins.html

    我认为你可以使用

    type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)
    

    这些内置函数执行原子比较和交换。也就是说,如果 *ptr 的当前值为 oldval,然后将 newval 写入 *ptr。

    如果比较成功并且newval,则“bool”版本返回true 被写了。 “val”版本返回 *ptr 之前的内容 操作。

    type __sync_lock_test_and_set (type *ptr, type value, ...)
    

    正如英特尔所描述的,这个内置不是传统的测试和设置 操作,而是原子交换操作。它 将值写入 *ptr,并返回 *ptr 之前的内容。

    许多目标仅对此类锁提供最低限度的支持,并且不支持完整的交换操作。在这种情况下,目标可能支持 减少的功能在这里存储的唯一有效值是 立即数 1. 实际存储在 *ptr 中的确切值是 实现定义。

    这个内置不是一个完整的屏障,而是一个获取屏障。这意味着内置函数之后的引用不能移动到(或 推测)在内置之前,但以前的内存存储可能不会 仍然是全局可见的,并且以前的内存负载可能还不是 满意。

    【讨论】:

    • 我想使用sync_lock,我在看原子的页面,从不看url!从那里我可以去gcc.gnu.org/onlinedocs 并检查他们引入原子内置函数的版本(4.7.4 手册!)谢谢!
    猜你喜欢
    • 2013-01-18
    • 2010-11-12
    • 2020-02-25
    • 2014-09-19
    • 1970-01-01
    • 1970-01-01
    • 2016-02-09
    • 2012-05-14
    • 1970-01-01
    相关资源
    最近更新 更多