成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

剖析Disruptor:為什么會這么快?(二)神奇的緩存行填充

開發(fā) 后端
我在上一篇文章中提到RingBuffer后,我們收到一些關(guān)于RingBuffer中填充高速緩存行的評論和疑問。由于這個適合用漂亮的圖片來說明,所以我想這是下一個我該解決的問題了。

我們經(jīng)常提到一個短語Mechanical Sympathy,這個短語也是Martin博客的標(biāo)題(譯注:Martin Thompson),Mechanical Sympathy講的是底層硬件是如何運(yùn)作的,以及與其協(xié)作而非相悖的編程方式。(注解:sympathy這里是共鳴、一致的含義)

我在上一篇文章中提到RingBuffer后,我們收到一些關(guān)于RingBuffer中填充高速緩存行的評論和疑問。由于這個適合用漂亮的圖片來說明,所以我想這是下一個我該解決的問題了。

(譯注:Martin Thompson很喜歡用Mechanical Sympathy這個短語,這個短語源于賽車駕駛,它反映了駕駛員對于汽車有一種天生的感覺,所以他們對于如何最佳的駕御它非常有感覺。)

計(jì)算機(jī)入門

我喜歡在LMAX工作的原因之一是,在這里工作讓我明白從大學(xué)和A Level Computing所學(xué)的東西實(shí)際上還是有意義的。做為一個開發(fā)者你可以逃避不去了解CPU,數(shù)據(jù)結(jié)構(gòu)或者大O符號 —— 而我用了10年的職業(yè)生涯來忘記這些東西。但是現(xiàn)在看來,如果你知道這些知識并應(yīng)用它,你能寫出一些非常巧妙和非常快速的代碼。

因此,對在學(xué)校學(xué)過的人是種復(fù)習(xí),對未學(xué)過的人是個簡單介紹。但是請注意,這篇文章包含了大量的過度簡化。

CPU是你機(jī)器的心臟,最終由它來執(zhí)行所有運(yùn)算和程序。主內(nèi)存(RAM)是你的數(shù)據(jù)(包括代碼行)存放的地方。本文將忽略硬件驅(qū)動和網(wǎng)絡(luò)之類的東西,因?yàn)镈isruptor的目標(biāo)是盡可能多的在內(nèi)存中運(yùn)行。

CPU和主內(nèi)存之間有好幾層緩存,因?yàn)榧词怪苯釉L問主內(nèi)存也是非常慢的。如果你正在多次對一塊數(shù)據(jù)做相同的運(yùn)算,那么在執(zhí)行運(yùn)算的時候把它加載到離CPU很近的地方就有意義了(比如一個循環(huán)計(jì)數(shù)-你不想每次循環(huán)都跑到主內(nèi)存去取這個數(shù)據(jù)來增長它吧)。

 

越靠近CPU的緩存越快也越小。所以L1緩存很小但很快(譯注:L1表示一級緩存),并且緊靠著在使用它的CPU內(nèi)核。L2大一些,也慢一些,并且 仍然只能被一個單獨(dú)的 CPU 核使用。L3在現(xiàn)代多核機(jī)器中更普遍,仍然更大,更慢,并且被單個插槽上的所有 CPU 核共享。最后,你擁有一塊主存,由全部插槽上的所有 CPU 核共享。

當(dāng)CPU執(zhí)行運(yùn)算的時候,它先去L1查找所需的數(shù)據(jù),再去L2,然后是L3,最后如果這些緩存中都沒有,所需的數(shù)據(jù)就要去主內(nèi)存拿。走得越遠(yuǎn),運(yùn)算耗費(fèi)的時間就越長。所以如果你在做一些很頻繁的事,你要確保數(shù)據(jù)在L1緩存中。

Martin和Mike的 QCon presentation演講中給出了一些緩存未命中的消耗數(shù)據(jù):

從CPU到 大約需要的 CPU 周期 大約需要的時間
主存   約60-80納秒
QPI 總線傳輸
(between sockets, not drawn)
  約20ns
L3 cache 約40-45 cycles, 約15ns
L2 cache 約10 cycles, 約3ns
L1 cache 約3-4 cycles, 約1ns
寄存器 1 cycle  

如果你的目標(biāo)是讓端到端的延遲只有 10毫秒,而其中花80納秒去主存拿一些未命中數(shù)據(jù)的過程將占很重的一塊。

緩存行

現(xiàn)在需要注意一件有趣的事情,數(shù)據(jù)在緩存中不是以獨(dú)立的項(xiàng)來存儲的,如不是一個單獨(dú)的變量,也不是一個單獨(dú)的指針。緩存是由緩存行組成的,通常是 64字節(jié)(譯注:這篇文章發(fā)表時常用處理器的緩存行是64字節(jié)的,比較舊的處理器緩存行是32字節(jié)),并且它有效地引用主內(nèi)存中的一塊地址。一個Java 的long類型是8字節(jié),因此在一個緩存行中可以存8個long類型的變量。

(為了簡化,我將忽略多級緩存)

非常奇妙的是如果你訪問一個long數(shù)組,當(dāng)數(shù)組中的一個值被加載到緩存中,它會額外加載另外7個。因此你能非常快地遍歷這個數(shù)組。事實(shí)上,你可以非常快速的遍歷在連續(xù)的內(nèi)存塊中分配的任意數(shù)據(jù)結(jié)構(gòu)。我在第一篇關(guān)于ring buffer的文章中順便提到過這個,它解釋了我們的ring buffer使用數(shù)組的原因。

因此如果你數(shù)據(jù)結(jié)構(gòu)中的項(xiàng)在內(nèi)存中不是彼此相鄰的(鏈表,我正在關(guān)注你呢),你將得不到免費(fèi)緩存加載所帶來的優(yōu)勢。并且在這些數(shù)據(jù)結(jié)構(gòu)中的每一個項(xiàng)都可能會出現(xiàn)緩存未命中。

不過,所有這種免費(fèi)加載有一個弊端。設(shè)想你的long類型的數(shù)據(jù)不是數(shù)組的一部分。設(shè)想它只是一個單獨(dú)的變量。讓我們稱它為head,這么稱呼它其實(shí)沒有什么原因。然后再設(shè)想在你的類中有另一個變量緊挨著它。讓我們直接稱它為tail。現(xiàn)在,當(dāng)你加載head到緩存的時候,你也免費(fèi)加載了tail

聽想來不錯。直到你意識到tail正在被你的生產(chǎn)者寫入,而head正在被你的消費(fèi)者寫入。這兩個變量實(shí)際上并不是密切相關(guān)的,而事實(shí)上卻要被兩個不同內(nèi)核中運(yùn)行的線程所使用。

設(shè)想你的消費(fèi)者更新了head的值。緩存中的值和內(nèi)存中的值都被更新了,而其他所有存儲head的緩存行都會都會失效,因?yàn)槠渌彺嬷?code>head不是最新值了。請記住我們必須以整個緩存行作為單位來處理(譯注:這是CPU的實(shí)現(xiàn)所規(guī)定的,詳細(xì)可參見深入分析Volatile的實(shí)現(xiàn)原理),不能只把head標(biāo)記為無效。

現(xiàn)在如果一些正在其他內(nèi)核中運(yùn)行的進(jìn)程只是想讀tail的值,整個緩存行需要從主內(nèi)存重新讀取。那么一個和你的消費(fèi)者無關(guān)的線程讀一個和head無關(guān)的值,它被緩存未命中給拖慢了。

當(dāng)然如果兩個獨(dú)立的線程同時寫兩個不同的值會更糟。因?yàn)槊看尉€程對緩存行進(jìn)行寫操作時,每個內(nèi)核都要把另一個內(nèi)核上的緩存塊無效掉并重新讀取里面的數(shù)據(jù)。你基本上是遇到兩個線程之間的寫沖突了,盡管它們寫入的是不同的變量。

這叫作“偽共享”(譯注:可以理解為錯誤的共享),因?yàn)槊看文阍L問head你也會得到tail,而且每次你訪問tail,你也會得到head。這一切都在后臺發(fā)生,并且沒有任何編譯警告會告訴你,你正在寫一個并發(fā)訪問效率很低的代碼。

解決方案-神奇的緩存行填充

你會看到Disruptor消除這個問題,至少對于緩存行大小是64字節(jié)或更少的處理器架構(gòu)來說是這樣的(譯注:有可能處理器的緩存行是128字 節(jié),那么使用64字節(jié)填充還是會存在偽共享問題),通過增加補(bǔ)全來確保ring buffer的序列號不會和其他東西同時存在于一個緩存行中。

  1. public long p1, p2, p3, p4, p5, p6, p7; // cache line padding 
  2.     private volatile long cursor = INITIAL_CURSOR_VALUE; 
  3.     public long p8, p9, p10, p11, p12, p13, p14; // cache line padding 

因此沒有偽共享,就沒有和其它任何變量的意外沖突,沒有不必要的緩存未命中。

在你的Entry類中也值得這樣做,如果你有不同的消費(fèi)者往不同的字段寫入,你需要確保各個字段間不會出現(xiàn)偽共享。

修改:Martin寫了一個從技術(shù)上來說更準(zhǔn)確更詳細(xì)的關(guān)于偽共享的文章,并且發(fā)布了性能測試結(jié)果。

原文鏈接:http://ifeve.com/disruptor-padding/

譯文鏈接:http://ifeve.com/disruptor-cacheline-padding/

責(zé)任編輯:陳四芳 來源: http://ifeve.com
相關(guān)推薦

2013-06-14 10:12:22

共享并行

2013-06-19 10:55:40

Disruptor并發(fā)框架

2013-06-17 14:41:10

Disruptor并發(fā)編程

2020-03-30 15:05:46

Kafka消息數(shù)據(jù)

2020-02-27 21:03:30

調(diào)度器架構(gòu)效率

2024-02-26 21:15:20

Kafka緩存參數(shù)

2020-02-27 15:44:41

Nginx服務(wù)器反向代理

2023-08-29 07:46:08

Redis數(shù)據(jù)ReHash

2020-10-15 09:19:36

Elasticsear查詢速度

2021-05-27 20:56:51

esbuild 工具JavaScript

2012-08-17 10:01:07

云計(jì)算

2022-05-23 10:11:36

HTTP緩存

2023-03-21 08:02:36

Redis6.0IO多線程

2022-01-04 08:54:32

Redis數(shù)據(jù)庫數(shù)據(jù)類型

2023-11-02 10:22:29

gRPC后端通信

2021-03-18 14:34:34

達(dá)達(dá)集團(tuán)京東云電商

2024-07-24 08:38:07

2020-04-27 07:13:37

Nginx底層進(jìn)程

2024-11-26 08:52:34

SQL優(yōu)化Kafka

2017-06-06 16:30:55

戴爾交付保障
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 国产一区黄色 | 精品一区二区三区在线观看国产 | 久久99精品久久久久久青青日本 | 久久精品国产一区 | 亚洲国产精品人人爽夜夜爽 | 在线一区观看 | 狠狠操狠狠干 | 黄色毛片一级 | 亚洲国产中文字幕 | 久久久久网站 | av一区二区三区在线观看 | 国产一区二区三区免费 | 91在线网站 | 成人一区二 | 五月激情综合网 | 6080亚洲精品一区二区 | 日本免费视频在线观看 | 一区二区蜜桃 | 久久久久成人精品免费播放动漫 | 日韩在线免费 | 亚洲高清视频一区二区 | 一区二区三区在线免费观看 | 激情五月激情综合网 | 欧美成人专区 | 久久久国产精品入口麻豆 | 91精品国产欧美一区二区 | 日韩在线观看一区 | 欧美操操操 | 一区二区不卡视频 | 国产日韩欧美一区二区 | 亚洲精品在线观看网站 | www.47久久青青 | 国产色 | 国产不卡一 | 91精品国产91久久综合桃花 | 欧美精品在线一区二区三区 | 久久久一区二区三区四区 | 日韩在线免费电影 | 日韩在线播放一区 | 偷拍自拍网址 | 亚洲成人午夜电影 |