java - 如果线程修改的变量被标记为 volatile,为什么 false 共享是一个问题

标签 java multithreading caching

我一直在看 Martin Thompson 的文章。这是对错误共享的解释。

http://mechanical-sympathy.blogspot.co.uk/2011/07/false-sharing.html

    public final class FalseSharing
    implements Runnable
    {
        public final static int NUM_THREADS = 4; // change
        public final static long ITERATIONS = 500L * 1000L * 1000L;
        private final int arrayIndex;

        private static VolatileLong[] longs = new VolatileLong[NUM_THREADS];


        static
        {    
            for (int i = 0; i < longs.length; i++)
            {
                longs[i] = new VolatileLong();
            }
        }

        public FalseSharing(final int arrayIndex)
        {
            this.arrayIndex = arrayIndex;
        }

        public static void main(final String[] args) throws Exception
        {
            final long start = System.nanoTime();
            runTest();
            System.out.println("duration = " + (System.nanoTime() -start));
        }

        private static void runTest() throws InterruptedException
        {
            Thread[] threads = new Thread[NUM_THREADS];

            for (int i = 0; i < threads.length; i++)
            {
                threads[i] = new Thread(new FalseSharing(i));
            }

            for (Thread t : threads)
            {
                t.start();
            }

            for (Thread t : threads)
            {
                t.join();
            }
        }

        public void run()
        {
            long i = ITERATIONS + 1;
            while (0 != --i)
            {
                longs[arrayIndex].value = i;
            }
        }

        public final static class VolatileLong
        {
            public volatile long value = 0L;
            public long p1, p2, p3, p4, p5, p6; // comment out
        }
    }

该示例演示了多个线程使彼此的缓存行无效时所经历的速度减慢,即使每个线程只更新一个变量也是如此。

BlockqFigure 1. above illustrates the issue of false sharing. A thread running on core 1 wants to update variable X while a thread on core 2 wants to update variable Y. Unfortunately these two hot variables reside in the same cache line. Each thread will race for ownership of the cache line so they can update it. If core 1 gets ownership then the cache sub-system will need to invalidate the corresponding cache line for core 2. When Core 2 gets ownership and performs its update, then core 1 will be told to invalidate its copy of the cache line. This will ping pong back and forth via the L3 cache greatly impacting performance. The issue would be further exacerbated if competing cores are on different sockets and additionally have to cross the socket interconnect.

我的问题如下。如果所有被更新的变量都是易变的,为什么这个填充会导致性能提升?我的理解是 volatile 变量总是写入和读取主内存。因此,我假设在此示例中对任何变量的每次写入和读取都会导致刷新当前核心缓存行。

所以根据我的理解。如果线程一使线程二的缓存行无效,这对线程二来说是不明显的,直到它从自己的缓存行中读取一个值。它读取的值是一个易变值,因此这会有效地使缓存变脏,从而导致从主内存读取。

我的理解哪里出了问题?

谢谢

最佳答案

If all the variables being updated are volatile, why does this padding cause a performance increase?

所以这里发生了两件事:

  1. 我们正在处理一组 VolatileLong 对象,每个线程都在自己的 VolatileLong 上工作。 (参见 private final int arrayIndex)。
  2. 每个 VolatileLong 对象都有一个 volatile 字段。

volatile 访问意味着线程必须同时使 cache "line" 无效。保存着他们的 volatile long value 并且他们需要锁定那个缓存行来更新它。如文章所述,缓存行通常约为 64 字节左右。

文章说,通过向 VolatileLong 对象添加填充,它会将每个线程锁定的对象移动到不同 缓存行中。因此,即使不同的线程在分配它们的 volatile long value 时仍然跨越内存障碍,它们位于不同的缓存行中,因此不会导致过多的 L2 缓存带宽。

总而言之,性能提升的发生是因为即使线程仍然锁定它们的缓存行以更新 volatile 字段,这些锁现在位于不同的内存块上,因此它们不会与其他线程的锁并导致缓存失效。

关于java - 如果线程修改的变量被标记为 volatile,为什么 false 共享是一个问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31183194/

相关文章:

php - 使用 htaccess 和 modrewrite 将整个 php 输出缓存到静态 html

java - 不缓存的Guava Cache

java - java 验证码 1.7

java - 在数组中使用循环和随机字符java

java - 如何消除3维java数组中的数组项

java - 为什么在 onCreate() 中创建一个新的 Thread 来做 I/O 仍然会出错?

java - java.time.ZoneId 是否有原因不包括 ZoneIds 的枚举?

java - 为什么在 Joshua Bloch Effective Java Example 中双重检查锁定快 25%

sql-server - SQL数据库触发器线程安全吗?

docker - docker build --no-cache 会构建不同的层吗?