使用内存屏障需要这些代码(并发C代码)吗?

问题描述 投票:0回答:1
  1. 我在项目 libuv 1.3.0 中找到了这些代码。但我不明白为什么需要内存屏障(对于编译器)。

    static int uv__async_make_pending(int* pending) {
      /* Do a cheap read first. */
      if (ACCESS_ONCE(int, *pending) != 0)
        return 1;
    
      /* Micro-optimization: use atomic memory operations to detect if we've been
       * preempted by another thread and don't have to make an expensive syscall.
       * This speeds up the heavily contended case by about 1-2% and has little
       * if any impact on the non-contended case.
       *
       * Use XCHG instead of the CMPXCHG that __sync_val_compare_and_swap() emits
       * on x86, it's about 4x faster. It probably makes zero difference in the
       * grand scheme of things but I'm OCD enough not to let this one pass.
       */
    #if defined(__i386__) || defined(__x86_64__)
      {
        unsigned int val = 1;
        __asm__ __volatile__ ("xchgl %0, %1"
                             : "+r" (val)
                             : "m"  (*pending));
        return val != 0;
      }
    #elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 0)
      return __sync_val_compare_and_swap(pending, 0, 1) != 0;
    #else
      ACCESS_ONCE(int, *pending) = 1;
      return 0;
    #endif
    }
    
  2. 我们可以通过不同的环境来简化这些代码。对于 x86:

    static int uv__async_make_pending(int* pending) {
      if (ACCESS_ONCE(int, *pending) != 0)
        return 1;
    
      {
        unsigned int val = 1;
        __asm__ __volatile__ ("xchgl %0, %1"
                             : "+r" (val)
                             : "m"  (*pending));
        return val != 0;
      }
    }
    

    对于 GNU C 4:

    static int uv__async_make_pending(int* pending) {
      if (ACCESS_ONCE(int, *pending) != 0)
        return 1;
    
      return __sync_val_compare_and_swap(pending, 0, 1) != 0;
    }
    

    对于其他人:

    static int uv__async_make_pending(int* pending) {
      if (ACCESS_ONCE(int, *pending) != 0)
        return 1;
    
      ACCESS_ONCE(int, *pending) = 1;
      return 0;
    }
    
  3. 我以最后一个为例。如果我消除它的记忆障碍:

    static int uv__async_make_pending(int* pending) {
      if (*pending != 0)
        return 1;
    
      *pending = 1;
      return 0;
    }
    

    上面的代码用gcc编译后会不会出错?我不知道gcc会做什么优化。

  4. 我直接google了代码,但是没有结果来解释为什么需要内存屏障。

  5. 我google了内存屏障,但是没有结果来解释我何时需要对特定代码使用内存屏障。我了解什么是记忆障碍。

  6. 我有goole的C语言无锁编程,但是没有结果来解释编译器如何优化代码。

c gcc optimization concurrency memory-barriers
1个回答
0
投票

@Jérôme Richard,@Peter Cordes 感谢您的意见。我想我已经弄清楚了这个问题。这里的ACCESS_ONE(内存屏障)保证对挂起的变量进行内存读取或写入,而不是寄存器。 因为我把全部精力都放在指令记录上,而忽略了寄存器替代内存访问的优化。在错误的方向上越走越远。当另一个人陷入无限循环时,有人给出简单的提示是很重要的。

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