一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

高性能緩存設(shè)計(jì):如何解決緩存?zhèn)喂蚕韱?wèn)題

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

掃碼添加小助手

加入工程師交流群

在多核高并發(fā)場(chǎng)景下,緩存?zhèn)喂蚕恚‵alse Sharing) 是導(dǎo)致性能驟降的“隱形殺手”。當(dāng)不同線程頻繁修改同一緩存行(Cache Line)中的獨(dú)立變量時(shí),CPU緩存一致性協(xié)議會(huì)強(qiáng)制同步整個(gè)緩存行,引發(fā)無(wú)效化風(fēng)暴,使看似無(wú)關(guān)的變量操作拖慢整體效率。本文從緩存結(jié)構(gòu)原理出發(fā),通過(guò)實(shí)驗(yàn)代碼復(fù)現(xiàn)偽共享問(wèn)題(耗時(shí)從3709ms優(yōu)化至473ms),解析其底層機(jī)制;同時(shí)深入剖析高性能緩存庫(kù) Caffeine 如何通過(guò) 內(nèi)存填充技術(shù)(120字節(jié)占位變量)隔離關(guān)鍵字段,以及 JDK 1.8 的 @Contended 注解如何以“空間換時(shí)間”策略高效解決偽共享問(wèn)題,揭示緩存一致性優(yōu)化的核心思想與實(shí)踐價(jià)值,為開(kāi)發(fā)者提供性能調(diào)優(yōu)的關(guān)鍵思路。

偽共享

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

接下來(lái)我們討論并驗(yàn)證在 CPU 緩存中是如何發(fā)生偽共享問(wèn)題的,首先我們需要先介紹一下 CPU 的緩存結(jié)構(gòu),如下圖所示:

wKgZPGhiYXSAWBAQAAKTcpGKIOw367.png

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

wKgZO2hiYXWAX1oFAAI12kQOkt8607.png

核心 1 不斷地訪問(wèn)和更新值 X,核心 2 則不斷地訪問(wèn)和更新值 Y,事實(shí)上每當(dāng)有核心對(duì)某一緩存行中的數(shù)據(jù)進(jìn)行修改時(shí),都會(huì)導(dǎo)致其他核心的緩存行失效,從而導(dǎo)致其他核心需要重新加載緩存行數(shù)據(jù),進(jìn)而導(dǎo)致性能下降,這也就是我們上文中所說(shuō)的緩存?zhèn)喂蚕韱?wèn)題。接下來(lái)我們用一段代碼來(lái)驗(yàn)證下緩存?zhèn)喂蚕韱?wèn)題造成的性能損失,如下所示:

public class TestFalseSharing {

    static class Pointer {
        // 兩個(gè) volatile 變量,保證可見(jiàn)性
        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();

        // 啟動(dòng)兩個(gè)線程,分別對(duì) x 和 y 進(jìn)行自增 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);
    }

}

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

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 {
        // ...
    }

}

本次任務(wù)執(zhí)行耗時(shí)為 473ms,性能得到了極大的提升?,F(xiàn)在我們已經(jīng)清楚的了解了緩存?zhèn)喂蚕韱?wèn)題,接下來(lái)我們討論下在 Caffeine 中是如何解決緩存?zhèn)喂蚕韱?wèn)題的。

Caffeine 對(duì)緩存?zhèn)喂蚕韱?wèn)題的解決方案

在 緩存之美:萬(wàn)文詳解 Caffeine 實(shí)現(xiàn)原理 中我們提到過(guò),負(fù)責(zé)記錄寫(xiě)后任務(wù)的 WriterBuffer 數(shù)據(jù)結(jié)構(gòu)的類(lèi)繼承關(guān)系如下所示:

wKgZPGhiYXaAPMq1AArX0SqWOsQ148.png

如圖中標(biāo)紅的類(lèi)所示,它們都是用來(lái)解決偽共享問(wèn)題的,我們以 BaseMpscLinkedArrayQueuePad1 為例來(lái)看下它的實(shí)現(xiàn):

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 {
    // 生產(chǎn)者操作索引(并不對(duì)應(yīng)緩沖區(qū) producerBuffer 中索引位置)
    protected long producerIndex;
}

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

其實(shí)除了 Caffeine 中有解決緩存?zhèn)喂蚕韱?wèn)題的方案外,在 JDK 1.8 中引入了 @Contended 注解,它也可以解決緩存?zhèn)喂蚕韱?wèn)題,如下所示為它在 ConcurrentHashMap 中的應(yīng)用:

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

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

其中的內(nèi)部類(lèi) CounterCell 被標(biāo)記了 @sun.misc.Contended 注解,表示該類(lèi)中的字段會(huì)與其他類(lèi)的字段相隔離,如果類(lèi)中有多個(gè)字段,實(shí)際上該類(lèi)中的變量間是不隔離的,這些字段可能被分配到同一緩存行中。因?yàn)?CounterCell 中只有一個(gè)字段,所以它會(huì)被被分配到一個(gè)緩存行中,剩余緩存行容量被空白內(nèi)存填充,本質(zhì)上也是一種以空間換時(shí)間的策略。這樣其他變量的變更就不會(huì)影響到 CounterCell 中的變量了,從而避免了緩存?zhèn)喂蚕韱?wèn)題。

這個(gè)注解不僅能標(biāo)記在類(lèi)上,還能標(biāo)記在字段上,拿我們的的代碼來(lái)舉例:

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 {
        // ...
    }

}

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

再談偽共享

避免偽共享的主要方法是代碼檢查,而且偽共享可能不太容易被識(shí)別出來(lái),因?yàn)橹挥性诰€程訪問(wèn)的是不同且碰巧在主存中相鄰的全局變量時(shí)才會(huì)出現(xiàn)偽共享問(wèn)題,線程的局部存儲(chǔ)或者局部變量不會(huì)是偽共享的來(lái)源。此外,解決偽共享問(wèn)題的本質(zhì)是以空間換時(shí)間,所以并不適用于在大范圍內(nèi)解決該問(wèn)題,否則會(huì)造成大量的內(nèi)存浪費(fèi)。

巨人的肩膀

維基百科 - 偽共享

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

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

博客園 - CPU Cache 與緩存行

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

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    11075

    瀏覽量

    216976
  • 緩存
    +關(guān)注

    關(guān)注

    1

    文章

    246

    瀏覽量

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

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    MCU緩存設(shè)計(jì)

    MCU 設(shè)計(jì)通過(guò)優(yōu)化指令與數(shù)據(jù)的訪問(wèn)效率,顯著提升系統(tǒng)性能并降低功耗,其核心架構(gòu)與實(shí)現(xiàn)策略如下: 一、緩存類(lèi)型與結(jié)構(gòu) 指令緩存(I-Cache)與數(shù)據(jù)緩存(D-Cache)? I-Ca
    的頭像 發(fā)表于 05-07 15:29 ?353次閱讀

    Nginx緩存配置詳解

    Nginx 是一個(gè)功能強(qiáng)大的 Web 服務(wù)器和反向代理服務(wù)器,它可以用于實(shí)現(xiàn)靜態(tài)內(nèi)容的緩存,緩存可以分為客戶端緩存和服務(wù)端緩存
    的頭像 發(fā)表于 05-07 14:03 ?565次閱讀
    Nginx<b class='flag-5'>緩存</b>配置詳解

    nginx中強(qiáng)緩存和協(xié)商緩存介紹

    強(qiáng)緩存直接告訴瀏覽器:在緩存過(guò)期前,無(wú)需與服務(wù)器通信,直接使用本地緩存
    的頭像 發(fā)表于 04-01 16:01 ?368次閱讀

    緩存與不帶緩存的固態(tài)硬盤(pán)有什么區(qū)別

    延遲、高可靠性和低噪音等優(yōu)點(diǎn),逐漸取代了傳統(tǒng)的機(jī)械硬盤(pán),成為市場(chǎng)的主流選擇。而固態(tài)硬盤(pán)中的緩存技術(shù),更是提升其性能的關(guān)鍵因素之一。本文將深入探討固態(tài)硬盤(pán)的定義、結(jié)構(gòu)、工作原理,以及帶緩存與不帶
    的頭像 發(fā)表于 02-06 16:35 ?2350次閱讀

    鴻蒙原生頁(yè)面高性能解決方案上線OpenHarmony社區(qū) 助力打造高性能原生應(yīng)用

    NEXT的原生頁(yè)面高性能解決方案,從頁(yè)面滑動(dòng)、跳轉(zhuǎn)及應(yīng)用冷啟動(dòng)等關(guān)鍵環(huán)節(jié),為開(kāi)發(fā)者提供全面的支持。目前,這些解決方案均已上線OpenHarmony開(kāi)源社區(qū),可在OpenHarmony三方庫(kù)中心倉(cāng)進(jìn)行搜索,歡迎開(kāi)發(fā)者多多使用和共建,打造更極致性能的鴻蒙應(yīng)用,共建
    發(fā)表于 01-02 18:00

    緩存對(duì)大數(shù)據(jù)處理的影響分析

    緩存對(duì)大數(shù)據(jù)處理的影響顯著且重要,主要體現(xiàn)在以下幾個(gè)方面: 一、提高數(shù)據(jù)訪問(wèn)速度 在大數(shù)據(jù)環(huán)境中,數(shù)據(jù)存儲(chǔ)通常采用分布式存儲(chǔ)系統(tǒng),數(shù)據(jù)量龐大,直接從存儲(chǔ)系統(tǒng)中讀取數(shù)據(jù)會(huì)存在較高的延遲。而通過(guò)緩存技術(shù)
    的頭像 發(fā)表于 12-18 09:45 ?766次閱讀

    HTTP緩存頭的使用 本地緩存與遠(yuǎn)程緩存的區(qū)別

    HTTP緩存頭是一組HTTP響應(yīng)頭,它們控制瀏覽器和中間代理服務(wù)器如何緩存網(wǎng)頁(yè)內(nèi)容。合理使用HTTP緩存頭可以顯著提高網(wǎng)站的加載速度和性能,減少服務(wù)器的負(fù)載。 1. HTTP
    的頭像 發(fā)表于 12-18 09:41 ?461次閱讀

    Web緩存的類(lèi)型及功能分析

    隨著互聯(lián)網(wǎng)的迅速發(fā)展,用戶對(duì)網(wǎng)絡(luò)內(nèi)容的訪問(wèn)需求日益增長(zhǎng)。為了提高用戶體驗(yàn)和降低服務(wù)器負(fù)擔(dān),Web緩存技術(shù)應(yīng)運(yùn)而生。Web緩存通過(guò)存儲(chǔ)重復(fù)請(qǐng)求的數(shù)據(jù),減少了對(duì)原始服務(wù)器的訪問(wèn)次數(shù),從而加快了數(shù)據(jù)傳輸
    的頭像 發(fā)表于 12-18 09:35 ?775次閱讀

    緩存技術(shù)在軟件開(kāi)發(fā)中的應(yīng)用

    在現(xiàn)代軟件開(kāi)發(fā)中,隨著數(shù)據(jù)量的爆炸性增長(zhǎng)和用戶對(duì)響應(yīng)速度的高要求,緩存技術(shù)成為了提升系統(tǒng)性能的重要手段。緩存技術(shù)通過(guò)將數(shù)據(jù)存儲(chǔ)在離用戶更近的位置,減少數(shù)據(jù)訪問(wèn)延遲,提高數(shù)據(jù)處理速度,從而優(yōu)化
    的頭像 發(fā)表于 12-18 09:32 ?694次閱讀

    什么是緩存(Cache)及其作用

    處理器和主存儲(chǔ)器之間的存儲(chǔ)系統(tǒng),其主要目的是減少處理器訪問(wèn)主存儲(chǔ)器所需的時(shí)間。由于處理器的運(yùn)行速度遠(yuǎn)遠(yuǎn)高于主存儲(chǔ)器的訪問(wèn)速度,這種速度差異會(huì)導(dǎo)致處理器在等待數(shù)據(jù)時(shí)出現(xiàn)空閑,從而降低整體性能。緩存通過(guò)存儲(chǔ)最近或頻繁訪問(wèn)的數(shù)據(jù)來(lái)緩
    的頭像 發(fā)表于 12-18 09:28 ?1.2w次閱讀

    探討移動(dòng)設(shè)備中的緩存文件管理

    /O性能和存儲(chǔ)壽命。結(jié)果表明其具有很好的實(shí)用價(jià)值。 ? ?? 背景 由于應(yīng)用程序的動(dòng)態(tài)特性和整體系統(tǒng)優(yōu)化,大部分移動(dòng)應(yīng)用程序都需要從網(wǎng)絡(luò)中下載文件或數(shù)據(jù)。 即使現(xiàn)代通信網(wǎng)絡(luò)具有更高的帶寬,許多應(yīng)用程序仍然嚴(yán)重依賴移動(dòng)設(shè)備上緩存的數(shù)據(jù),以避免通過(guò)網(wǎng)絡(luò)重新下載
    的頭像 發(fā)表于 11-28 11:50 ?1066次閱讀
    探討移動(dòng)設(shè)備中的<b class='flag-5'>緩存</b>文件管理

    緩存之美——如何選擇合適的本地緩存

    Guava cache是Google開(kāi)發(fā)的Guava工具包中一套完善的JVM本地緩存框架,底層實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)類(lèi)似于ConcurrentHashMap,但是進(jìn)行了更多的能力拓展,包括緩存過(guò)期時(shí)間設(shè)置、緩存容量設(shè)置、多種淘汰策略、
    的頭像 發(fā)表于 11-17 14:24 ?816次閱讀
    <b class='flag-5'>緩存</b>之美——如何選擇合適的本地<b class='flag-5'>緩存</b>?

    TMS320C64x在高性能DSP應(yīng)用中的高速緩存使用情況

    電子發(fā)燒友網(wǎng)站提供《TMS320C64x在高性能DSP應(yīng)用中的高速緩存使用情況.pdf》資料免費(fèi)下載
    發(fā)表于 10-21 09:43 ?0次下載
    TMS320C64x在<b class='flag-5'>高性能</b>DSP應(yīng)用中的高速<b class='flag-5'>緩存</b>使用情況

    DSP指令緩存性能OMAP5912

    電子發(fā)燒友網(wǎng)站提供《DSP指令緩存性能OMAP5912.pdf》資料免費(fèi)下載
    發(fā)表于 10-16 10:16 ?0次下載
    DSP指令<b class='flag-5'>緩存</b><b class='flag-5'>性能</b>OMAP5912

    什么是CPU緩存?它有哪些作用?

    CPU緩存(Cache Memory)是計(jì)算機(jī)系統(tǒng)中一個(gè)至關(guān)重要的組成部分,它位于CPU與內(nèi)存之間,作為兩者之間的臨時(shí)存儲(chǔ)器。CPU緩存的主要作用是減少CPU訪問(wèn)內(nèi)存所需的時(shí)間,從而提高系統(tǒng)的整體性能。以下將詳細(xì)闡述CPU
    的頭像 發(fā)表于 08-22 14:54 ?6094次閱讀