国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

高性能緩存設計:如何解決緩存偽共享問題

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2025-07-01 15:01 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

在多核高并發場景下,緩存偽共享(False Sharing) 是導致性能驟降的“隱形殺手”。當不同線程頻繁修改同一緩存行(Cache Line)中的獨立變量時,CPU緩存一致性協議會強制同步整個緩存行,引發無效化風暴,使看似無關的變量操作拖慢整體效率。本文從緩存結構原理出發,通過實驗代碼復現偽共享問題(耗時從3709ms優化至473ms),解析其底層機制;同時深入剖析高性能緩存庫 Caffeine 如何通過 內存填充技術(120字節占位變量)隔離關鍵字段,以及 JDK 1.8 的 @Contended 注解如何以“空間換時間”策略高效解決偽共享問題,揭示緩存一致性優化的核心思想與實踐價值,為開發者提供性能調優的關鍵思路。

偽共享

偽共享(False sharing)是一種會導致性能下降的使用模式,最常見于現代多處理器CPU緩存中。當不同線程頻繁修改同一緩存行(Cache Line)中不同變量時,由于CPU緩存一致性協議(如MESI)會強制同步整個緩存行,導致線程間無實際數據競爭的邏輯變量被迫觸發緩存行無效化(Invalidation),引發頻繁的內存訪問和性能下降。盡管這些變量在代碼層面彼此獨立,但因物理內存布局相鄰,共享同一緩存行,造成“虛假競爭”,需通過內存填充或字段隔離使其獨占緩存行解決。

接下來我們討論并驗證在 CPU 緩存中是如何發生偽共享問題的,首先我們需要先介紹一下 CPU 的緩存結構,如下圖所示:

wKgZPGhiYXSAWBAQAAKTcpGKIOw367.png

CPU Cache 通常分為大小不等的三級緩存,分別為 L1 Cache、L2 Cache、L3 Cache,越靠近 CPU 的緩存,速度越快,容量也越小。CPU Cache 實際上由很多個緩存行 Cache Line 組成,通常它的大小為 64 字節(或 128 字節),是 CPU 從內存中 讀取數據的基本單位,如果訪問一個 long[] 數組,當其中一個值被加載到緩存中時,它會額外加載另外 7 個元素到緩存中。那么我們考慮這樣一種情況,CPU 的兩個核心分別訪問和修改統一緩存行中的數據,如下圖所示:

wKgZO2hiYXWAX1oFAAI12kQOkt8607.png

核心 1 不斷地訪問和更新值 X,核心 2 則不斷地訪問和更新值 Y,事實上每當有核心對某一緩存行中的數據進行修改時,都會導致其他核心的緩存行失效,從而導致其他核心需要重新加載緩存行數據,進而導致性能下降,這也就是我們上文中所說的緩存偽共享問題。接下來我們用一段代碼來驗證下緩存偽共享問題造成的性能損失,如下所示:

public class TestFalseSharing {

    static class Pointer {
        // 兩個 volatile 變量,保證可見性
        volatile long x;
        volatile long y;

        @Override
        public String toString() {
            return "x=" + x + ", y=" + y;
        }
    }

    @Test
    public void testFalseSharing() throws InterruptedException {
        Pointer pointer = new Pointer();

        // 啟動兩個線程,分別對 x 和 y 進行自增 1億 次的操作
        long start = System.currentTimeMillis();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 100_000_000; i++) {
                pointer.x++;
            }
        });
        Thread t2 = new Thread(() -?> {
            for (int i = 0; i < 100_000_000; i++) {
                pointer.y++;
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println(System.currentTimeMillis() - start);
        System.out.println(pointer);
    }

}

這種情況下會發生緩存的偽共享,x 和 y 被加載到同一緩存行中,當其中一個值被修改時,會使另一個核心中的該緩存行失效并重新加載,代碼執行實際耗時為 3709ms。如果我們將 x 變量后再添加上 7 個 long 型的元素,使得變量 x 和變量 y 分配到不同的緩存行中,那么理論上性能將得到提升,我們實驗一下:

public class TestFalseSharing {

    static class Pointer {
        volatile long x;
        long p1, p2, p3, p4, p5, p6, p7;
        volatile long y;

        @Override
        public String toString() {
            return "x=" + x + ", y=" + y;
        }
    }

    @Test
    public void testFalseSharing() throws InterruptedException {
        // ...
    }

}

本次任務執行耗時為 473ms,性能得到了極大的提升。現在我們已經清楚的了解了緩存偽共享問題,接下來我們討論下在 Caffeine 中是如何解決緩存偽共享問題的。

Caffeine 對緩存偽共享問題的解決方案

在 緩存之美:萬文詳解 Caffeine 實現原理 中我們提到過,負責記錄寫后任務的 WriterBuffer 數據結構的類繼承關系如下所示:

wKgZPGhiYXaAPMq1AArX0SqWOsQ148.png

如圖中標紅的類所示,它們都是用來解決偽共享問題的,我們以 BaseMpscLinkedArrayQueuePad1 為例來看下它的實現:

abstract class BaseMpscLinkedArrayQueuePad1 extends AbstractQueue {
    byte p000, p001, p002, p003, p004, p005, p006, p007;
    byte p008, p009, p010, p011, p012, p013, p014, p015;
    byte p016, p017, p018, p019, p020, p021, p022, p023;
    byte p024, p025, p026, p027, p028, p029, p030, p031;
    byte p032, p033, p034, p035, p036, p037, p038, p039;
    byte p040, p041, p042, p043, p044, p045, p046, p047;
    byte p048, p049, p050, p051, p052, p053, p054, p055;
    byte p056, p057, p058, p059, p060, p061, p062, p063;
    byte p064, p065, p066, p067, p068, p069, p070, p071;
    byte p072, p073, p074, p075, p076, p077, p078, p079;
    byte p080, p081, p082, p083, p084, p085, p086, p087;
    byte p088, p089, p090, p091, p092, p093, p094, p095;
    byte p096, p097, p098, p099, p100, p101, p102, p103;
    byte p104, p105, p106, p107, p108, p109, p110, p111;
    byte p112, p113, p114, p115, p116, p117, p118, p119;
}

abstract class BaseMpscLinkedArrayQueueProducerFields extends BaseMpscLinkedArrayQueuePad1 {
    // 生產者操作索引(并不對應緩沖區 producerBuffer 中索引位置)
    protected long producerIndex;
}

可以發現在這個類中定義了 120 個字節變量,這樣緩存行大小不論是 64 字節還是 128 字節,都能保證字段間的隔離。如圖中所示 AbstractQueue 和 BaseMpscLinkedArrayQueueProducerFields 中的變量一定會 被分配到不同的緩存行 中。同理,借助 BaseMpscLinkedArrayQueuePad2 中的 120 個字節變量,BaseMpscLinkedArrayQueueProducerFields 和 BaseMpscLinkedArrayQueueConsumerFields 中的變量也會被分配到不同的緩存行中,這樣就避免了緩存的偽共享問題。

其實除了 Caffeine 中有解決緩存偽共享問題的方案外,在 JDK 1.8 中引入了 @Contended 注解,它也可以解決緩存偽共享問題,如下所示為它在 ConcurrentHashMap 中的應用:

public class ConcurrentHashMap extends AbstractMap
        implements ConcurrentMap, Serializable {
    // ...
    
    @sun.misc.Contended
    static final class CounterCell {
        volatile long value;

        CounterCell(long x) {
            value = x;
        }
    }
}

其中的內部類 CounterCell 被標記了 @sun.misc.Contended 注解,表示該類中的字段會與其他類的字段相隔離,如果類中有多個字段,實際上該類中的變量間是不隔離的,這些字段可能被分配到同一緩存行中。因為 CounterCell 中只有一個字段,所以它會被被分配到一個緩存行中,剩余緩存行容量被空白內存填充,本質上也是一種以空間換時間的策略。這樣其他變量的變更就不會影響到 CounterCell 中的變量了,從而避免了緩存偽共享問題。

這個注解不僅能標記在類上,還能標記在字段上,拿我們的的代碼來舉例:

public class TestFalseSharing {

    static class Pointer {
        @Contended("cacheLine1")
        volatile long x;
        //        long p1, p2, p3, p4, p5, p6, p7;
        @Contended("cacheLine2")
        volatile long y;

        @Override
        public String toString() {
            return "x=" + x + ", y=" + y;
        }
    }
    
    @Test
    public void testFalseSharing() throws InterruptedException {
        // ...
    }

}

它可以指定內容來 定義多個字段間的隔離關系。我們使用注解將這兩個字段定義在兩個不同的緩存行中,執行結果耗時與顯示聲明字段占位耗時相差不大,為 520ms。另外需要注意的是,要想使注解 Contended 生效,需要添加 JVM 參數 -XX:-RestrictContended。

再談偽共享

避免偽共享的主要方法是代碼檢查,而且偽共享可能不太容易被識別出來,因為只有在線程訪問的是不同且碰巧在主存中相鄰的全局變量時才會出現偽共享問題,線程的局部存儲或者局部變量不會是偽共享的來源。此外,解決偽共享問題的本質是以空間換時間,所以并不適用于在大范圍內解決該問題,否則會造成大量的內存浪費。

巨人的肩膀

維基百科 - 偽共享

小林coding - 2.3 如何寫出讓 CPU 跑得更快的代碼

知乎 - 雜談 什么是偽共享(false sharing)

博客園 - CPU Cache 與緩存行

博客園 - 偽共享(false sharing),并發編程無聲的性能殺手

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • cpu
    cpu
    +關注

    關注

    68

    文章

    11277

    瀏覽量

    224934
  • 緩存
    +關注

    關注

    1

    文章

    248

    瀏覽量

    27758
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    探秘DS2731:緩存內存電池備份管理IC的卓越性能與應用

    探秘DS2731:緩存內存電池備份管理IC的卓越性能與應用 在電子設備的設計中,電源管理是一個至關重要的環節,尤其是對于需要可靠備份電源的應用場景。今天,我們就來深入探討一款功能強大的緩存內存電池
    的頭像 發表于 02-24 16:40 ?302次閱讀

    DRAM緩存真有那么重要嗎?天碩工業級SSD固態硬盤實測告訴你答案!

    在國產固態硬盤推薦榜中,推薦五花八門,很多用戶疑惑DRAM 緩存 SSD 值得買嗎?不同方案的國產 SSD 性能對比差距到底有多大?價格差距不小,性能也各說各的好。要弄清兩者的實際差異,先看國產 SSD
    的頭像 發表于 01-19 16:49 ?262次閱讀

    C語言的緩沖區(緩存)詳解

    緩沖區又稱為緩存,它是內存空間的一部分。也就是說,在內存空間中預留了一定的存儲空間,這些存儲空間用來緩沖輸入或輸出的數據,這部分預留的空間就叫做緩沖區。   緩沖區根據其對應的是輸入設備還是輸出設備
    發表于 01-14 07:30

    數據全復用高性能池化層設計思路分享

    大家好,本團隊此次分享的內容為可實現數據全復用高性能池化層設計思路,核心部分主要由以下3個部分組成; 1.SRAM讀取模塊;——池化使用的存儲為SRAM 基于SRAM讀與寫時序,約束池化模塊讀與寫
    發表于 10-29 07:10

    串口DMA發送有緩存嗎?

    串口DMA發送有緩存嗎, 我是從ringbuffer取出來,放到申請的緩存里,啟動串口DMA發送,然后就釋放了。暫時沒發現什么問題。 用的drv_usart.c是這個版本
    發表于 10-10 06:14

    Redis緩存的經典問題和解決方案

    用戶瘋狂查詢數據庫中不存在的數據,每次查詢都繞過緩存直接打到數據庫,導致數據庫壓力驟增。
    的頭像 發表于 08-20 16:24 ?777次閱讀

    緩存之美:萬文詳解 Caffeine 實現原理(上)

    用于統計元素訪問頻率的 Count-Min Sketch 數據結構、理解內存屏障和如何避免緩存共享問題、MPSC 多線程設計模式、高性能緩存
    的頭像 發表于 08-05 14:49 ?700次閱讀
    <b class='flag-5'>緩存</b>之美:萬文詳解 Caffeine 實現原理(上)

    本地緩存 Caffeine 中的時間輪(TimeWheel)是什么?

    我們詳細介紹了 Caffeine 緩存添加元素和讀取元素的流程,并詳細解析了配置固定元素數量驅逐策略的實現原理。在本文中我們將主要介紹 配置元素過期時間策略的實現原理 ,補全 Caffeine
    的頭像 發表于 08-05 14:48 ?609次閱讀
    本地<b class='flag-5'>緩存</b> Caffeine 中的時間輪(TimeWheel)是什么?

    harmony-utils之CacheUtil,緩存工具類

    harmony-utils之CacheUtil,緩存工具類
    的頭像 發表于 07-04 16:36 ?493次閱讀

    請問如何在C++中使用NPU上的模型緩存

    無法確定如何在 C++ 中的 NPU 上使用模型緩存
    發表于 06-24 07:25

    HarmonyOS5云服務技術分享--云緩存快速上手指南

    連接失敗 五、總結 通過本文,相信大家對AGC云緩存的接入已有了全面認識。無論是Node.js的輕量級方案,還是Java中的三種靈活選擇,都能幫助業務快速實現高性能緩存。如果在實踐中遇到問題,記得回看
    發表于 05-22 18:37

    MCU緩存設計

    MCU 設計通過優化指令與數據的訪問效率,顯著提升系統性能并降低功耗,其核心架構與實現策略如下: 一、緩存類型與結構 指令緩存(I-Cache)與數據緩存(D-Cache)? I-Ca
    的頭像 發表于 05-07 15:29 ?1108次閱讀

    Nginx緩存配置詳解

    Nginx 是一個功能強大的 Web 服務器和反向代理服務器,它可以用于實現靜態內容的緩存緩存可以分為客戶端緩存和服務端緩存
    的頭像 發表于 05-07 14:03 ?1245次閱讀
    Nginx<b class='flag-5'>緩存</b>配置詳解

    高速SSD存儲系統中數據緩存控制器整體頂層設計

    數據緩存控制器主要實現了對大量突發數據的緩存、AXI4接口與AXI4-Stream接口之間的轉換和NVMe命令的生成等功能。這里主要介紹相關開發流程。
    的頭像 發表于 04-14 10:46 ?778次閱讀
    高速SSD存儲系統中數據<b class='flag-5'>緩存</b>控制器整體頂層設計

    nginx中強緩存和協商緩存介紹

    緩存直接告訴瀏覽器:在緩存過期前,無需與服務器通信,直接使用本地緩存
    的頭像 發表于 04-01 16:01 ?975次閱讀