你猜 為什么A64為什么沒(méi)有LDM和STM指令了,而是用LDP跟STP呢?

一、前言

我們知道在Arm Arch32里面有個(gè)突發(fā)傳輸指令LDM、STM,也就是說(shuō)可以一次傳輸多個(gè)值,到底是多少個(gè)呢?根據(jù)手冊(cè)里面所說(shuō):加載和存儲(chǔ)多個(gè)寄存器。寄存器r0到r15的任何組合均可在ARM狀態(tài)下傳輸。

也就是說(shuō)傳輸?shù)酵ㄓ眉拇嫫骼锩嬉淮慰梢詡鬏敽芏喟。?/p>

但是到了Arch64里面就取消掉這個(gè)指令了,取而代之的是LDP和STP,固定的一次最多只能取2個(gè)值,為何呢?

這篇文章接下來(lái)的部分就是為了探究這個(gè)問(wèn)題的!

二、資料搜集

資料:《Cortex_A57_Software_Optimization_Guide_external》
中說(shuō)到了用LDP做memcopy的好處是可以盡可能地利用load和store的pipeline:


4.5 Load/Store Throughput

The Cortex-A57 processor includes separate load and store pipelines, which allow it to execute one load μop and one store μop every cycle. .

譯:由于a57是有分別獨(dú)立存在的加載、存儲(chǔ)流水線,也就是說(shuō)配合多發(fā)射就可能在一個(gè)cycle內(nèi)同時(shí)執(zhí)行兩條(ldr、str)指令。

To achieve maximum throughput for memory copy (or similar loops), one should do the following.

實(shí)現(xiàn)內(nèi)存復(fù)制最大吞吐的指導(dǎo)思想如下:

  • Unroll the loop to include multiple load and store operations per iteration, minimizing the overheads of looping.(老生常談:循環(huán)展開(kāi),使得循環(huán)部分產(chǎn)生的過(guò)沖減少,其實(shí)就是減少循環(huán)邏輯部分在整個(gè)指令執(zhí)行數(shù)里面的比例,從而提高準(zhǔn)確率以及減少分支預(yù)測(cè)的次數(shù)。
  • Use discrete, non-writeback forms of load and store instructions (such as LDRD and STRD), interleaving them so that one load and one store operation may be performed each cycle. Avoid load-/store-multiple instruction encodings (such as LDM and STM), which lead to separated bursts of load and store μops which may not allow concurrent utilization of both the load and store pipelines.

使用離散的、非寫(xiě)回的內(nèi)存指令,并間隔開(kāi)來(lái)從而最大化利用ld/st的雙pipeline特性;避免使用LDM跟STM,因?yàn)檫@樣子的話就會(huì)產(chǎn)生一些分散的突發(fā)傳輸,從而無(wú)法合理利用雙pipeline特性。

The following example shows a recommended instruction sequence for a long memory copy in AArch32 state:

Loop_start:
    SUBS r2,r2,#64
    LDRD r3,r4,[r1,#0] 
    STRD r3,r4,[r0,#0] 
    LDRD r3,r4,[r1,#8] 
    STRD r3,r4,[r0,#8] 
    LDRD r3,r4,[r1,#16] 
    STRD r3,r4,[r0,#16] 
    LDRD r3,r4,[r1,#24] 
    STRD r3,r4,[r0,#24] 
    LDRD r3,r4,[r1,#32] 
    STRD r3,r4,[r0,#32] 
    LDRD r3,r4,[r1,#40] 
    STRD r3,r4,[r0,#40] 
    LDRD r3,r4,[r1,#48] 
    STRD r3,r4,[r0,#48] 
    LDRD r3,r4,[r1,#56] 
    STRD r3,r4,[r0,#56] 
    ADD r1,r1,#64
    ADD r0,r0,#64 
    BGT Loop_start

A recommended copy routine for AArch64 would look similar to the sequence above, but would use LDP/STP instructions.

小結(jié):上面的代碼就是利用了循環(huán)展開(kāi)、間隔LDRD和STRD從而利用雙pipeline實(shí)現(xiàn)該兩條指令并發(fā)執(zhí)行。最后頁(yè)也說(shuō)了下,在Arch64模式下也是類似處理利用LDP/STP指令。


4.7 Non-Temporal Loads/Stores

The ARM v8-A architecture provides load/store non-temporal pair instructions (LDNP/STNP) that provide a hint to the memory system that an access is non-temporal or streaming, and unlikely to be repeated in the near future.

也就是說(shuō)在ARM v8-A架構(gòu)里面LDNP/STNP指令是non-temporal的,也就是說(shuō)當(dāng)你用這條指令的時(shí)候,就會(huì)給存儲(chǔ)子系統(tǒng)傳達(dá)這么一個(gè)消息:我現(xiàn)在處理的內(nèi)存數(shù)據(jù)是流式的、非臨時(shí)的,在不久的將來(lái)也是不會(huì)使用的。

Versions of the Cortex-A57 processor prior to r1p3 do not prefetch or cache non-temporal data and hence, there could be a performance degradation when using non-temporal loads/stores instead of normal loads/stores. Ignoring the non-temporal hint will avoid this degradation. To this effect, it is recommended that bit 52 of the CPU auxiliary control register (CPUACTLR_EL1[52]) be always enabled.

從上面的資料我們大概知道用LDP跟STP可以最大化利用ARM v8-A架構(gòu)的存儲(chǔ)雙pipeline架構(gòu),但是其實(shí)里面還有很多細(xì)節(jié)的信息我們不清楚,因此我們繼續(xù)進(jìn)一步深挖:

(我想以后無(wú)論我干什么事情,都要是有這種耐心、嚴(yán)謹(jǐn)、深入實(shí)踐的精神,應(yīng)該人生是不會(huì)太困惑的吧!用心做好每一件事,不忘初心?。?/code>

  • non-writeback指令代表什么意思?
  • non-temporal指令代表什么意思?
  • LDP/STP和LDNP/STNP指令的區(qū)別及各自特點(diǎn)?

三、思考&繼續(xù)前行

把握了大的框架思想后,接下來(lái)就是填充細(xì)節(jié)了,不用急,沉下心來(lái),慢慢來(lái)!

上面的writeback什么的是cache的一些特性參數(shù),因此我們先看cache部分怎么解釋。

這里提前介紹下我們的資料及其特點(diǎn):

  • 《ARM? Architecture Reference Manual ARMv8, for ARMv8-A architecture profile》Printed on: December 19, 2017

    • 這個(gè)是Arm Archv8-A profile文檔,跟一般芯片數(shù)據(jù)手冊(cè)一樣介紹寄存器信息的,指令具體怎么用,有什么細(xì)節(jié)就去這里查了!也就是說(shuō)是給原廠的底層開(kāi)發(fā)人員看的,或者是驅(qū)動(dòng)開(kāi)發(fā)人員看的,總之很底層了!參考對(duì)比當(dāng)年的單片機(jī)開(kāi)發(fā)人員。
  • 《ARM? Cortex?-A Series Version: 1.0 Programmer’s Guide for ARMv8-A》Copyright ? 2015 ARM. All rights reserved.

    • 這里講的很直白了,就是編程手冊(cè),給一般程序員看的,里面講了ARM的一些宏觀層面上的模塊,也就是粗略介紹了下有哪些模塊,有哪些特性,單數(shù)具體的參數(shù)就沒(méi)有了!比如指令參數(shù)以及指令使用這里就沒(méi)有了!

3.1 cache

我這里就不從cache的架構(gòu)一路講下來(lái)了,具體細(xì)節(jié)參看我以前寫(xiě)的cache專題文章。

先大概講下哈:
我們寫(xiě)的時(shí)候是不是有兩種情況,一種是寫(xiě)命中一種是寫(xiě)miss:

  • 寫(xiě)命中:更新cache中的副本后

    • write-through:直接一層一層往底層寫(xiě)回去,這樣子回浪費(fèi)很大的總線帶寬?。?/li>
    • write-back: 盡可能地推遲更新,只有當(dāng)這個(gè)cache line被踢除的時(shí)候才把它寫(xiě)回到下一層中,注意不是寫(xiě)回最底層喲,而是下一層;這樣做的好處是減少總線流量,缺點(diǎn)是這樣的操作復(fù)雜性就增高了??!成本高了!
  • 寫(xiě)不命中

    • write-allocate: 讀取低一層中的塊來(lái)更新這個(gè)告訴緩存塊,注意都是以塊也就是cache line為單位哈!
    • non-write-allocate: 避開(kāi)高速緩存,直接把這個(gè)字寫(xiě)入到低一層中。

直寫(xiě)高速緩存 --通常-->非寫(xiě)分配
寫(xiě)回高速緩存 --通常-->寫(xiě)分配

接下來(lái)主要就cache的一些策略結(jié)合官方文檔進(jìn)行介紹。

  • 《ARM? Cortex?-A Series Version: 1.0 Programmer’s Guide for ARMv8-A》
ARM? Cortex?-A Series Version: 1.0 Programmer’s Guide for ARMv8-A

高速緩存策略使我們能夠描述何時(shí)應(yīng)該將一行分配給數(shù)據(jù)緩存,以及當(dāng)一個(gè)store指令在數(shù)據(jù)緩存中被執(zhí)行時(shí),應(yīng)該發(fā)生什么情況。

cache的分配策略如下:

  • 寫(xiě)分配:也就是當(dāng)你只寫(xiě)1 byte miss的時(shí)候,硬件就不管你三七二十一,直接產(chǎn)生一個(gè)突發(fā)傳輸給memory子系統(tǒng),然后獲取到一個(gè)cache line的數(shù)據(jù),cache line全部獲取到位之后才把你要的那1 byte數(shù)據(jù)給你,怎么樣是不是聽(tīng)著就很浪費(fèi)。這就好比在古代:楊貴妃說(shuō)想要吃荔枝了,這可不得了,一道圣旨發(fā)下,底下的百官一陣折騰弄來(lái)了一大車(chē)的荔枝,但是最后到楊貴妃嘴里的最多就開(kāi)始的那一盆子吧~你說(shuō)是不是勞民傷財(cái),浪費(fèi)資源??!
  • 讀分配:當(dāng)讀miss發(fā)生的時(shí)候就給其分配一個(gè)cache line。

3.2 prefetch

預(yù)取就是告訴memory子系統(tǒng),我有個(gè)數(shù)據(jù)馬上要用,你給安排一下!嘿嘿~像不像真實(shí)世界的走后門(mén)?。√崆按蚵曊泻?,提前準(zhǔn)備好!VIP通道~~

這里分別介紹了在64bit跟32bit模式下指令的差異,我們主要關(guān)注的是預(yù)取的策略policy字段:

  • KEEP就是保持在cache內(nèi)部:又叫temporal prefetch,就是正常的額cache 分配策略;
  • STRM就是流式處理,數(shù)據(jù)不會(huì)存儲(chǔ)在cache里面的,也就是從cache上面流過(guò),不留痕跡:non-temporal prefetch也就是說(shuō)內(nèi)存數(shù)據(jù)只用一次的,你不要放到cache 里面來(lái)浪費(fèi)空間了,不要影響我的cache hit rate了。

3.3 LDP/STP

首先告訴你64bit模式下是沒(méi)有32bit模式下的LDM跟STM指令的:

特點(diǎn)如下:

  • 32bit模式下也是有LDRD跟STD指令的,但是在64bit模式下任意的兩個(gè)整型寄存器都是可以讀或者寫(xiě)的。
  • 數(shù)據(jù)的讀寫(xiě)是內(nèi)存里面的連續(xù)位置的;
  • 地址模式更嚴(yán)格:只能base + offset的模式進(jìn)行訪存,offset范圍是正負(fù)127范圍(7bit);
  • 與32bit的LDRD跟STD指令不同的是:64bit模式下支持非對(duì)齊訪問(wèn)。
  • 注意:LDP是支持SIMD的啊!

下面可以看到各種允許的訪問(wèn)方式:


這里對(duì)比看一下你就知道上述的兩個(gè)參考文件的區(qū)別,這是profile文件,里面講了指令具體怎么用的:

3.4 LDNP/STNP

  • LDNP跟STNP是Armv8才有的機(jī)制,non-temporal也就是說(shuō)我讀寫(xiě)的時(shí)候數(shù)據(jù)是不會(huì)存到cache里面來(lái)的!也就是說(shuō)假如我只要2byte的數(shù)據(jù),通常系統(tǒng)會(huì)加載1個(gè)cache line進(jìn)來(lái),但是只要用了LDNP這個(gè)指令,系統(tǒng)就不會(huì)一下加載一整個(gè)cache line進(jìn)來(lái)了,而是只加載2byte進(jìn)來(lái),從而減少總線流量,進(jìn)而可能減少取數(shù)據(jù)的時(shí)間;因此LDNP這種指令只適合那種數(shù)據(jù)量比較少的情況(一般小于一個(gè)cahche line),避免cache line加載的耗時(shí)耗費(fèi)資源的情況。

  • non-temporal加載和存儲(chǔ)能夠放松對(duì)訪存順序的要求,如下圖中的例子,LDNP可能比LDR要提前執(zhí)行(因?yàn)槲也恍枰M(jìn)行cache line fill操作啊,我是流式讀的呀,所以你LDR慢慢去cache line fill吧,我先把媳婦,哦不,數(shù)據(jù)給先娶回去了?。?,但是這里這樣子就出問(wèn)題了,因此要加barriar,從而保證執(zhí)行順序。

  • 只支持一種訪存模式;
  • 這個(gè)指令會(huì)告訴memory子系統(tǒng):我這是流式訪存,這個(gè)數(shù)據(jù)我暫時(shí)只用一次的,因此就不勞煩你把數(shù)據(jù)給cache 緩存起來(lái)了!但是基于memory子系統(tǒng)類型,可能支持讀預(yù)取和寫(xiě)打包加速等操作!
In addition, there is an exception to the usual memory ordering rules. 
If an address dependency exists between two memory reads, and a Load Non-temporal Pair instruction generated the second read, then in the absence of any other barrier mechanism to achieve order, the memory accesses can be observed in any order by the other observers within the shareability domain of the memory addresses being accessed.

此外,通常的內(nèi)存排序規(guī)則有一個(gè)例外。如果兩個(gè)內(nèi)存讀之間存在地址依賴關(guān)系,第二個(gè)讀指令是LDNP產(chǎn)生的,然后在沒(méi)有任何其他屏障機(jī)制來(lái)保證順序的情況下,這個(gè)可能就會(huì)亂序執(zhí)行的!

是不是說(shuō)可以利用這個(gè)來(lái)增大吞吐呢?

四、總結(jié)

  • 在Arm v8-A架構(gòu)里面因?yàn)橛袃蓚€(gè)分別獨(dú)立的ld、st pipeline,因此交叉使用LDP、STP指令可以并行使用pipeline,增大吞吐(因?yàn)閍rmv7一般是單個(gè)加載存儲(chǔ)單元,因此LDM、STM更有效);

  • LDNP、STNP是流式處理數(shù)據(jù)的,不會(huì)進(jìn)行cache line fill操作,因此適合少量數(shù)據(jù)處理的情形,減少總線流量,比如堆棧恢復(fù)的時(shí)候,我堆棧就那么幾個(gè)字節(jié)的數(shù)據(jù),你常規(guī)操作是要加載一個(gè)cache line回來(lái)的啊,污染cahche空間不說(shuō)還增大總線流量,因此我用LDNP更有效;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 目錄:1.數(shù)據(jù)依賴性2.程序順序規(guī)則3.重排序?qū)Χ嗑€程的影響4.編譯器重排序5.指令集并行的重排序6.內(nèi)存系統(tǒng)的重...
    西部小籠包閱讀 16,837評(píng)論 9 25
  • 在今天,我看了馴龍高手。因?yàn)槲覜Q定看一部電影,然后把電影的情節(jié)寫(xiě)到文章上。我爸給我選了馴龍高手,所以我就看馴龍高手...
    肖子巖閱讀 753評(píng)論 4 0
  • 沒(méi)有多進(jìn)程,即使CPU有多個(gè)核心,程序只是運(yùn)行在一個(gè)核心上,無(wú)法利用多進(jìn)程提升效率。5000萬(wàn)次加法,如果需要2....
    凱茜的老爸閱讀 922評(píng)論 1 2
  • http://blog.cocoachina.com/article/61433
    TsingQue閱讀 327評(píng)論 0 0
  • 我是一名職場(chǎng)菜鳥(niǎo),甚至可以說(shuō)是一個(gè)十足的傻小白。 即將畢業(yè),卻也有各種惆悵:不知道現(xiàn)在的選擇是否正確,更不知道是否...
    笨笨一直在努力閱讀 282評(píng)論 0 0

友情鏈接更多精彩內(nèi)容