<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          終于結(jié)束了!總結(jié)一下這次大賽.

          共 9019字,需瀏覽 19分鐘

           ·

          2021-08-25 15:50

          03d0c6abeefb676a7911e0da3c013b85.webp

          你好呀,我是歪歪。

          給大家分享一篇關(guān)于性能挑戰(zhàn)賽的文章,我看了之后受益匪淺。

          阿里云舉辦的大賽我從 2016 開始關(guān)注,每年的賽題我都仔細(xì)看了,并思考過解題思路。

          雖然每年都是陪跑,但是能讀懂排名靠前的參賽選手的比賽思路,也是一件非常有收獲的事情。

          共勉之。

          前言

          我參加了阿里云舉辦的《第三屆數(shù)據(jù)庫大賽創(chuàng)新上云性能挑戰(zhàn)賽–高性能分析型查詢引擎賽道》,傳送門:

          https://tianchi.aliyun.com/competition/entrance/531895/introduction

          截止到 8 月 20 日,終于結(jié)束了漫長的賽程。

          作為阿里云員工的我,按照賽題規(guī)定,只能參加初賽,不能參加復(fù)賽,出于不影響比賽的目的,終于等到了比賽完全結(jié)束,才動(dòng)筆寫下了這篇參賽總結(jié)。

          照例先說成績,這里貼一下排行榜,總共有 1446 只隊(duì)伍,可以看到不少學(xué)生和其他公司的員工都參賽了。

          b1de048a6074c681535aac4ba511df43.webp

          我的成績是第 14 名,內(nèi)部排名也是進(jìn)入了前五。

          先簡單點(diǎn)評(píng)下這次比賽吧。

          首先是主辦方的出題水平,還是非常高的。

          • 全程沒有改過賽題描述
          • 全程沒有清過榜單
          • 沒有暗改過數(shù)據(jù)
          • 賽題做到了“題面描述簡單,實(shí)現(xiàn)具有區(qū)分度”,讓不同水平的參賽者都得到了發(fā)揮
          • 評(píng)測友好,失敗不計(jì)算提交次數(shù)

          這些點(diǎn)要大大點(diǎn)一個(gè)贊,建議其他比賽的主辦方都來參考下,向這次出題的水準(zhǔn)看齊。

          不好的點(diǎn),我也吐槽下:

          • 內(nèi)部賽獎(jiǎng)勵(lì)太敷衍了,倒不如送我點(diǎn)公仔來的實(shí)在
          • 復(fù)賽延期了兩周,讓很多選手的肝有些吃不消

          這篇文章我覺得得先明確一個(gè)定位,復(fù)賽和初賽技術(shù)架構(gòu)相差是很大的,而我沒有參加復(fù)賽,并沒有發(fā)言權(quán),所以就不去詳細(xì)介紹最終的復(fù)賽方案了。

          我的這篇文章還是以初賽為主,一方面聊的話題也輕松些,另一方面初賽的架構(gòu)簡單一些,可以供那些希望參加性能挑戰(zhàn)賽而又苦于沒有學(xué)習(xí)資料的同學(xué)、初賽沒有找到優(yōu)化點(diǎn)的同學(xué)一些參考。

          賽題介紹eea6d6310845b12f7044dcda5c4ba7de.webp

          https://tianchi.aliyun.com/competition/entrance/531895/introduction

          題面可以說是非常簡單的,其實(shí)就是實(shí)現(xiàn)一個(gè)查詢第 N 大的數(shù)函數(shù)。

          它的輸入數(shù)據(jù)一共有兩列:

          803e225e497c0c9d4850857a8155659a.webp

          第一行是列名,第二行開始是列的數(shù)據(jù)。

          初賽測試數(shù)據(jù):只有一張表 lineitem,只有兩列 L_ORDERKEY (bigint), L_PARTKEY (bigint),數(shù)據(jù)量3億行,單線程查詢 10 次。

          如果你還不理解這個(gè)題面是什么意思,建議去看下官方提供的評(píng)測 demo:

          https://code.aliyun.com/analytic_db/2021-tianchi-contest-1

          本地就能運(yùn)行,debug 跑一遍,既能讀懂題意,也能得到一個(gè)baseline(盡管它不能提交,會(huì)爆內(nèi)存)。

          另外需要格外注意的一點(diǎn),千萬不要漏看賽題描述

          本題結(jié)合英特爾? 傲騰? 持久內(nèi)存技術(shù)(PMem),探索新介質(zhì)和新軟件系統(tǒng)上極致的持久化和性能

          我們的方案一定需要圍繞著存儲(chǔ)介質(zhì)的特性去設(shè)計(jì),你問我持久內(nèi)存 PMem 是啥?

          老實(shí)說,我參賽之前對這個(gè)新的技術(shù)也是一知半解,但為了成績,一定需要啃下這個(gè)技術(shù),下面我就先介紹下 PMem。

          持久內(nèi)存 PMem 介紹

          044482ff240d12211217b2c7dbfc515d.webp

          提到內(nèi)存(DRAM)、固態(tài)磁盤(SSD)、機(jī)械硬盤(HDD)這些概念,相信大多數(shù)人不會(huì)感到陌生,都能夠道出這幾個(gè)介質(zhì)的訪問速度差異。而持久內(nèi)存 PMem 這個(gè)概念,相對而言不太為人所知,的確,它是近幾年才興起的一個(gè)概念。

          持久內(nèi)存 (PMem) 是駐留在內(nèi)存總線上的固態(tài)高性能按字節(jié)尋址的內(nèi)存設(shè)備。PMem 位于內(nèi)存總線上,支持像 DRAM 一樣訪問數(shù)據(jù),這意味著它具備與 DRAM 相當(dāng)?shù)乃俣群脱舆t,而且兼具 NAND 閃存的非易失性。NVDIMM(非易失性雙列直插式內(nèi)存模塊)和 Intel 3D XPoint DIMM(也稱為 Optane DC 持久內(nèi)存模塊)是持久內(nèi)存技術(shù)的兩個(gè)示例。

          當(dāng)我們在討論這些存儲(chǔ)介質(zhì)時(shí),我們有哪些關(guān)注點(diǎn)呢?

          本節(jié)開頭的金字塔圖片的三條邊直觀地詮釋了眾多存儲(chǔ)介質(zhì)在造價(jià)、訪問延時(shí)、容量三方面的對比。我下面從訪問延時(shí)、造價(jià)和持久化特性三個(gè)方面做一下對比。

          訪問延時(shí)

          內(nèi)存 DRAM 的訪問延時(shí)在 80~100ns,固態(tài)硬盤 SSD 的訪問延時(shí)在 10~100us,而持久內(nèi)存介于兩者之間,比內(nèi)存慢 10 倍,比固態(tài)硬盤快 10~100 倍。

          造價(jià)

          目前為止,將 PMem 技術(shù)正式商用的公司,貌似只有 Intel,也就是本次比賽的贊助商。

          Intel optane DC persistent memory 是 Intel 推出的基于 3D Xpoint 技術(shù)的持久內(nèi)存產(chǎn)品,其代號(hào)為 Apace Pass (AEP)。

          所以大家今后如果看到其他人提到 AEP,基本心理就有數(shù)了,說的就是 PMem 這個(gè)存儲(chǔ)介質(zhì)。

          在某電商平臺(tái)看看這東西怎么賣的:

          a2e31c140aaff0c0b97cdabde56d5913.webp

          好家伙,128 * 4 條,賣 15000,折合下來,一根 PMem 就要 3000~4000。

          同時(shí)我們也注意到,傲騰系列的 PMem 產(chǎn)品最高規(guī)格也就只有 512G,基本佐證了金字塔中的 Capacity 這一維度,屬于內(nèi)存嫌大,磁盤嫌小的一個(gè)數(shù)值。

          持久化

          這東西咱們的電腦可以裝嗎?

          當(dāng)然可以,直接插在內(nèi)存條上就成。

          我們都知道內(nèi)存是易失性的存儲(chǔ),磁盤是持久化的存儲(chǔ),而介于兩者之間的持久內(nèi)存,持久化特性是什么樣的呢?

          這一點(diǎn),不能望文生義地認(rèn)為 PMem 就是持久化的,而是要看其工作模式:

          Memory Mode 和 AppDirect Mode。

          466a2108602c55a0a95d2a25865b0c2c.webp

          本文就不過多展開介紹了這兩種模式了。

          簡單來說,PMem 工作在 Memory Mode 時(shí),是易失性的,這時(shí)候,你需要使用專門的一套系統(tǒng)指令去進(jìn)行存取。

          PMem 工作在 AppDirect Mode 時(shí),可以直接把 PMem 當(dāng)成一塊磁盤來用,PMem 背后適配了一整套文件系統(tǒng)的指令,使得常規(guī)的文件操作可以完全兼容的跑在 PMem 之上。

          我花了這么大的篇幅介紹 PMem,仍然只介紹了 PMem 特性非常小的一部分,最多讓大家知道 PMem 是個(gè)啥,至于怎么利用好這塊盤,我后邊會(huì)花專門的一篇文章去介紹。

          回到賽題,盡管 intel 提供了一套 PMem 專用的 API:

          https://github.com/pmem/pmemkv-java

          但由于比賽限定了不能引入三方類庫。

          a0cac776f8920a70e0a28f506a9b9484.webp

          所以等于直接告訴了參賽選手,PMem 這塊盤是工作在 AppDirect Mode 之下的,大家可以完全把它當(dāng)成一塊磁盤去存取。

          這個(gè)時(shí)候,選手們就需要圍繞 PMem 的特性,去設(shè)計(jì)存儲(chǔ)引擎的架構(gòu),可能你在固態(tài)硬盤、常規(guī)文件操作中的一些認(rèn)知會(huì)被顛覆。

          這很正常,畢竟 PMem 的出現(xiàn),就是為了顛覆傳統(tǒng)存儲(chǔ)架構(gòu)而生的。

          在不能直接操作 PMem 的情況下選手們需要設(shè)計(jì) PMem 友好的架構(gòu)。

          賽題剖析

          此次的數(shù)據(jù)輸入方式和之前的比賽有很大的不同,選手們需要自行去解析文件,獲得輸入數(shù)據(jù),同時(shí)進(jìn)行處理,如何高效的處理文件是很大的一塊優(yōu)化點(diǎn)。

          初賽一共 3 億行數(shù)據(jù),一共 2 列,內(nèi)存一共 4 G,稍微計(jì)算下就會(huì)發(fā)現(xiàn),全部存儲(chǔ)在內(nèi)存中是存不下的(不考慮壓縮),所以需要用到 PMem 充當(dāng)存儲(chǔ)引擎。

          查詢的需求是查找到第 N 大的數(shù),所以我們的架構(gòu)一定是需要做到整體有序,允許局部無序。

          賽題數(shù)據(jù)的說明尤為重要:

          測試數(shù)據(jù)隨機(jī),均勻分布。

          看過我之前文章的讀者,應(yīng)當(dāng)敏銳地注意到了均勻分布這個(gè)關(guān)鍵詞,這意味著我們又可以使用數(shù)據(jù)的頭 n 位來分區(qū)了。

          這里先給出初賽的最終架構(gòu),明確下如何串聯(lián)各個(gè)流程:

          4a615ba4989034bd9d50c543dcb73c20.webp

          把大象放進(jìn)冰箱總共需要三步,這道題目僅僅多了一步。

          第一步:將輸入文件從邏輯上分成 12 等分,這樣 12 個(gè)線程可以并發(fā)讀取輸入文件。可以借助預(yù)處理程序,找到等分的邊界。

          第二步:每個(gè)線程都需要讀取各自的文件分片,將讀取到的文件流在內(nèi)存中解析成 long,并且需要根據(jù)逗號(hào)、換行符來區(qū)分出兩列數(shù)據(jù)。

          第三步:讀取到 long 之后,需要根據(jù)頭 n 位進(jìn)行分區(qū),例如選擇頭 8 位,可以獲得 2^(8-1) 即 128 個(gè)分區(qū),因?yàn)楸荣愔械臄?shù)據(jù)都是正數(shù),所以減了一個(gè)符號(hào)位。這樣分區(qū)之后,可以保證分區(qū)之間有序,分區(qū)內(nèi)部無序。

          第四步:獲取第 N 大的數(shù)字時(shí),可以直接根據(jù)分區(qū)內(nèi)的數(shù)據(jù)量,直接定位到最終在哪個(gè)分區(qū),這樣就可以確保只加載一部分?jǐn)?shù)據(jù)到內(nèi)存中。t1_pn ~ t12_pn 在邏輯上組成了 partitionN,將 partitionN 的數(shù)據(jù)加載進(jìn)內(nèi)存之后,這道題就變成:查詢無序數(shù)組中第 N 大數(shù)的問題了。

          實(shí)現(xiàn)細(xì)節(jié)

          以下的實(shí)現(xiàn)細(xì)節(jié),我會(huì)給出實(shí)現(xiàn)難度,以供大家參考,打分標(biāo)準(zhǔn):編碼實(shí)現(xiàn)難度,容不容易想到等綜合評(píng)分。

          多線程按分區(qū)讀文件(難度:2 顆星)

          輸入文件按行來分隔數(shù)據(jù),第一時(shí)間聯(lián)想到的就是 JDK 提供的 java.io.BufferedReader#readLine() 方法。

          但稍微懂點(diǎn)文件 IO 基礎(chǔ)的讀者都應(yīng)該意識(shí)到,文件 IO 要想快,一定得順序按塊讀,所以 readLine 這種方法,想都不用想,必定是低效的。

          那有人問了,博主,你給解釋解釋,什么叫按塊讀?

          最簡單的做法是按照固定 4kb 的 buffer 來讀取文件,在內(nèi)存中,自行判斷逗號(hào)、換行符來區(qū)分兩列數(shù)據(jù),不斷移動(dòng)這個(gè) 4kb 的塊,這就是按塊讀了。

          if?(readBufferArray[i]?==?'\n')?{
          }?

          if(readBufferArray[i]?==?',')?{
          }

          光是按塊讀還不夠,為了充分發(fā)揮 IO 特性,還可以用上多線程,按照上圖中的第一步,我的方案將文件分成了 12 份,這樣 12 個(gè)線程可以齊頭并進(jìn)地進(jìn)行讀取和解析。

          經(jīng)過測試,多線程比單線程要快 70 多秒,所以沒有使用多線程的選手,名次肯定不會(huì)高,這是一個(gè)通用優(yōu)化點(diǎn)。

          Long 轉(zhuǎn)換(難度:1 顆星)

          將文件中的字節(jié)讀取到內(nèi)存中,一定會(huì)經(jīng)過 byte[] 到 long 的轉(zhuǎn)換過程,千萬不要小看這個(gè)環(huán)節(jié),這可是眾多選手分?jǐn)?shù)的分水嶺。

          先來看下 demo 中給出的方案:

          String[]?columns?=?reader.readLine().split(",");
          Long.parseLong(row[i]);

          這里面存在兩個(gè)問題

          • 1.先轉(zhuǎn) String,再轉(zhuǎn)成 Long,多了一次無效轉(zhuǎn)換
          • 2.Long.parseLong 方法比較低效,有很多無用判斷

          我貼一下我方案的偽代碼:

          long?val?=?0;
          for?(int?i?=?0;?i?<?size;?i++)?{
          ????val?=?val?*?10?+?(readBuffer[i]?-?'0');
          }

          直接從字節(jié)數(shù)組中解析出 Long。

          解析出 Long 主要還是為了落盤的時(shí)候進(jìn)行數(shù)據(jù)對齊,主流方案應(yīng)該都會(huì)解析。

          按頭 n 位分桶落盤(難度:1 顆星)

          在讀取到一個(gè) Long 之后,我們可以按照數(shù)據(jù)的頭 n 位,將其寫入對應(yīng)的分區(qū)文件中。

          這其實(shí)也是一個(gè)通用的優(yōu)化點(diǎn),我在《華為云 TaurusDB 性能挑戰(zhàn)賽賽題總結(jié)》也介紹過,分區(qū)之后,可以保證分區(qū)之間有序,即 partition1 中的任意數(shù)據(jù)一定小于 partition2 中的任意數(shù)據(jù)。

          分區(qū)之間無序,主要是為了可以實(shí)現(xiàn)分區(qū)文件的順序?qū)憽?/p>

          至于 n 具體是多少,取決于我們想分多個(gè)區(qū)。

          分區(qū)太多,會(huì)導(dǎo)致整體寫入速度下降;分區(qū)太少,讀取階段加載的數(shù)據(jù)會(huì)過多,甚至可能導(dǎo)致內(nèi)存放不下。

          每個(gè)寫線程維護(hù)自己的分區(qū)文件(難度:3 顆星)

          在賽題剖析里面,我給出了我最終方案的流程圖,里面有一個(gè)細(xì)節(jié),每個(gè)讀取線程從 1/12 個(gè)文件分片中讀取解析到的 Long 數(shù)值,寫入了自己線程編號(hào)對應(yīng)的文件中,進(jìn)行落盤。

          并沒有采用寫入同一個(gè)分區(qū)文件這樣的設(shè)計(jì)。對比下兩種做法的利弊:

          • 寫線程寫入同一個(gè)分區(qū)文件。好處是讀取階段不需要聚合多份分區(qū)文件,壞處是多個(gè)線程寫入同一個(gè)分區(qū)需要加鎖,會(huì)導(dǎo)致競爭。
          • 寫線程寫入自己的分區(qū)文件。好處是不需要加鎖寫,壞處是讀取階段需要聚合讀取。

          也好理解,兩個(gè)方案的優(yōu)劣正好相反,稍微分析一下,由于初賽的查詢只有 10 次,所以聚合的開銷不會(huì)太大,再加上,我們本來就希望讀取能做到并發(fā),聚合沒有那么可怕。反而是寫入時(shí)加鎖導(dǎo)致的沖突,會(huì)嚴(yán)重浪費(fèi) CPU。

          該優(yōu)化點(diǎn),幫助我的方案從 80s 縮短到 50s。

          分支預(yù)測優(yōu)化(難度:4 顆星)

          這次比賽因?yàn)橛辛?PMem,導(dǎo)致瓶頸根本不出在 IO 上。

          以往比賽中,大家都是想盡一切方法,把 CPU 讓給 IO。

          而這次比賽,PMem 直接起飛了,導(dǎo)致大家需要考慮,怎么優(yōu)化 CPU。

          而 JVM 虛擬機(jī)的一系列機(jī)制中,就有很多注意事項(xiàng),是跟 CPU 優(yōu)化相關(guān)的。

          在解析 Long 時(shí),我們需要從 4kb 的讀緩沖區(qū)中解析出 Long 數(shù)值,由于文件中的數(shù)值是以不定長的字節(jié)數(shù)組形式出現(xiàn)的,我們只能通過判斷逗號(hào)、換行符來解析出數(shù)值,所以難免會(huì)寫出這樣的代碼:

          int?blockReadPosition?=?0;
          for?(int?i?=?0;?i?<?size;?i++)?{
          ????if?(readBufferArray[i]?==?'\n')?{
          ????????partRaceEngine.add(threadNo,?val);
          ????????val?=?0;
          ????????blockReadPosition?=?i?+?1;
          ????}?else?if(readBufferArray[i]?==?',')?{
          ????????orderRaceEngine.add(threadNo,?val);
          ????????val?=?0;
          ????????blockReadPosition?=?i?+?1;
          ????}?else?{
          ????????val?=?val?*?10?+?(readBufferArray[i]?-?'0');
          ????}
          }

          思考下,這段代碼會(huì)有什么邏輯問題嗎?

          當(dāng)然沒有,相信很多選手也會(huì)這么判斷。

          但不妨分析下,輸入文件大概有 10G 左右,所有的字節(jié)都會(huì)經(jīng)過 if 判斷一次,而實(shí)際上,大多數(shù)的字符并不是 \n 和 , 。

          這會(huì)導(dǎo)致 CPU 被浪費(fèi)在分支預(yù)測上。

          我的優(yōu)化思路也很簡單,直接用循環(huán),干掉 if/else 判斷

          for?(int?i?=?0;?i?<?size;?i++)?{
          ???byte?temp?=?readBufferArray[i];
          ???do?{
          ???????val?=?val?*?10?+?(temp?-?'0');
          ???????temp?=?readBufferArray[++i];
          ???}?while?(temp?!=?',');
          ???orderRaceEngine.add(threadNo,?val);
          ???val?=?0;
          ???//?skip?,
          ???i++;
          ???temp?=?readBufferArray[i];
          ???do?{
          ???????val?=?val?*?10?+?(temp?-?'0');
          ???????temp?=?readBufferArray[++i];
          ???}?while?(temp?!=?'\n');
          ???partRaceEngine.add(threadNo,?val);
          ???val?=?0;
          ???//?skip?\n
          }
          readPosition?+=?size;

          一般來說,再?zèng)]有辦法去掉 if/else 的前提下,我們可以遵循的一個(gè)最佳實(shí)踐是,將容易命中的條件放到最前面。

          該優(yōu)化幫助我從 48s 優(yōu)化到了 24s。

          另外,也可以利用數(shù)據(jù)特性,因?yàn)榇蠖鄶?shù)數(shù)據(jù)是 19 位的數(shù)字,可以直接判斷第 20 位是不是 , 或者 \n 從而減少分支預(yù)測的次數(shù)。

          quickSelect(難度:4 顆星)

          在查詢階段,查詢一個(gè)分區(qū)內(nèi)第 N 大的數(shù),最簡單的思路是排序之后直接返回,a[N],受到評(píng)測 demo 的影響,很多選手可能忽略了可以使用 quickSelect 算法。

          Quick Select 你可能沒聽過,但快速排序(Quick Sort)你肯定有所耳聞,其實(shí)他們兩個(gè)算法的作者都是 Hoare,并且思想也非常接近:

          選取一個(gè)基準(zhǔn)元素 pivot,將數(shù)組切分(partition)為兩個(gè)子數(shù)組,比 pivot 大的扔左子數(shù)組,比 pivot 小的扔右子數(shù)組,然后遞推地切分子數(shù)組。

          Quick Select 不同于 Quick Sort 之處在于其沒有對每個(gè)子數(shù)組做切分,而是對目標(biāo)子數(shù)組做切分。

          其次,Quick Select 與Quick Sort 一樣,是一個(gè)不穩(wěn)定的算法。

          pivot 選取直接影響了算法的好壞,最壞情況下的時(shí)間復(fù)雜度達(dá)到了 O(n2)。

          Quick Select 的 Java 實(shí)現(xiàn)如下:

          public?static?long?quickSelect(long[]?nums,?int?start,?int?end,?int?k)?{
          ????????if?(start?==?end)?{
          ????????????return?nums[start];
          ????????}
          ????????int?left?=?start;
          ????????int?right?=?end;
          ????????long?pivot?=?nums[(start?+?end)?/?2];
          ????????while?(left?<=?right)?{
          ????????????while?(left?<=?right?&&?nums[left]?>?pivot)?{
          ????????????????left++;
          ????????????}
          ????????????while?(left?<=?right?&&?nums[right]?<?pivot)?{
          ????????????????right--;
          ????????????}
          ????????????if?(left?<=?right)?{
          ????????????????long?temp?=?nums[left];
          ????????????????nums[left]?=?nums[right];
          ????????????????nums[right]?=?temp;
          ????????????????left++;
          ????????????????right--;
          ????????????}
          ????????}
          ????????if?(start?+?k?-?1?<=?right)?{
          ????????????return?quickSelect(nums,?start,?right,?k);
          ????????}
          ????????if?(start?+?k?-?1?>=?left)?{
          ????????????return?quickSelect(nums,?left,?end,?k?-?(left?-?start));
          ????????}
          ????????return?nums[right?+?1];
          ????}

          該優(yōu)化幫我從 24s 優(yōu)化到 17s。

          查詢階段多線程讀分區(qū)(難度:2 顆星)

          前文提到了為了避免寫入階段的沖突,每個(gè)線程維護(hù)了自己的分區(qū)文件,在查詢時(shí),則需要聚合多個(gè)線程的數(shù)據(jù)。

          這個(gè)時(shí)候不要忘記也可以多線程讀取,因?yàn)槌踬惖脑u(píng)測程序是單線程測評(píng)的,IO 無法打滿,需要我們控制多線程,充分利用 IO。

          該優(yōu)化幫我從 17s 優(yōu)化到了 15s。

          循環(huán)展開(難度:4 顆星)

          盡管得知我們可以知道字節(jié)數(shù)組的長度,從而用循環(huán)來解析出 Long,但根據(jù) JMH 的優(yōu)化項(xiàng)來看,手動(dòng)展開循環(huán),可以讓程序更加地快,例如像下面這樣。

          a3027914e505789008adde5e2546a15a.webp

          這樣的優(yōu)化大概僅僅能提升 1s~2s,甚至不到,但越是到前排,1~2s 的優(yōu)化就越會(huì)顯得彌足珍貴。

          總結(jié)

          還有很多之前我提到過的一些通用優(yōu)化技巧,例如順序讀寫、讀寫緩沖、對象復(fù)用等等,就不在這里繼續(xù)贅述了,盡管 PMem 和固態(tài)硬盤這兩種介質(zhì)有一定的差異,但這些優(yōu)化技巧依舊是通用的。

          因?yàn)檫@次比賽,IO 的速度實(shí)在是太快了,導(dǎo)致優(yōu)化的方向變成如何優(yōu)化 CPU,合理分配 CPU,讓 CPU 更多地分配給瓶頸操作,這是其他比賽中沒有過的經(jīng)歷。

          還有一些點(diǎn)是通過調(diào)參來實(shí)現(xiàn)的,例如文件分片數(shù),讀寫緩沖區(qū)的大小,讀寫的線程數(shù)等等,也會(huì)導(dǎo)致成績相差非常大,這就需要不斷地肝,不斷地 benchmark 了。

          不光是成功的優(yōu)化點(diǎn)值得分享,也拿一個(gè)失敗的優(yōu)化分享一下,例如,將一半的數(shù)據(jù)存儲(chǔ)在內(nèi)存中,最終發(fā)現(xiàn),申請內(nèi)存的時(shí)間,倒不如拿去進(jìn)行文件 IO,最終放棄了,可以見得在合理的架構(gòu)設(shè)計(jì)下,PMem 的表現(xiàn)的確彪悍,不輸于內(nèi)存存取。

          這次 ADB 的性能挑戰(zhàn)賽,雖然只參加了初賽,但收獲的技能點(diǎn)還是不少的。

          印象最深的便是 PMem 這塊盤的表現(xiàn)和我理解中的 SSD 還是有一定差距的,導(dǎo)致之前的一些經(jīng)驗(yàn)不能直接在這場比賽中運(yùn)用。

          我也大概了解了很多復(fù)賽前排選手使用到了很多的奇技淫巧,每一個(gè)看似奇葩的優(yōu)化點(diǎn)背后,可能都蘊(yùn)含著該選手對操作系統(tǒng)、文件系統(tǒng)、編程語言等方面超出常人的認(rèn)知,值得喝彩。

          感到遺憾的地方還是有的。

          這次比賽只能讓 PMem 工作在 APP Direct 模式下,沒有能夠真正做到顛覆性。

          如果有一場比賽,能夠支持 Memory Mode,那我應(yīng)該能收獲到對持久內(nèi)存更加深刻的認(rèn)知。

          我一直反復(fù)安利我的讀者盡可能地參加各類性能挑戰(zhàn)賽,特別是在校生、實(shí)習(xí)生或者剛進(jìn)入職場的新人,這種比賽是實(shí)踐的最好機(jī)會(huì),看書不是。

          好了,最后,我將我的代碼開源在了 github:

          https://github.com/lexburner/2021-tianchi-adb-race




          推薦???:幾行爛代碼,我賠了16萬。

          推薦???:吳某凡表示這題他熟。

          推薦???:這題答案不在源碼里...

          推薦???:就這樣,我走完了程序員的前五年...

          推薦???:Java如何綁定線程到指定CPU上執(zhí)行?

          我是 why,你也可以叫我小歪,一個(gè)主要寫代碼,經(jīng)常寫文章,偶爾拍視頻的程序猿。

          歡迎關(guān)注我呀。

          瀏覽 106
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  婷婷情色五月天 | 在线视频精品播放 | 国内自拍第一区二区三区 | 色秘 一区二区三区在线男奴 | 青青草视频免费在线播放 |