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

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

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

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

為什么在JVM x86平臺生成的機(jī)器代碼中會看到XMM寄存器?

冬至子 ? 來源:ImportNew ? 作者:ImportNew ? 2023-06-15 15:22 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

2. 問題

代碼中沒有浮點(diǎn)或矢量操作,為什么在 JVM x86 平臺生成的機(jī)器代碼中會看到 XMM 寄存器?

3. 理論

FPU 和矢量單元在現(xiàn)代 CPU 中無處不在。通常,它們會為 FPU 特定操作提供了備用寄存器。例如,英特爾 x86_64 平臺的 SSE 和 AVX 擴(kuò)展包含了一組豐富的 XMM、YMM 和 ZMM 寄存器供指令操作。

雖然非矢量指令集與矢量、非矢量寄存器通常不會正交,比如不能在 x86_64 上對 XMM 寄存器執(zhí)行通用 IMUL,但是這些寄存器仍然提供了一種存儲選項(xiàng)。即使不用于矢量計(jì)算,也可以在這些寄存器中存儲數(shù)據(jù)。

(1) 最極端的情況是把矢量寄存器當(dāng)緩沖用。

寄存器分配器的任務(wù)是在一個(gè)特定的編譯單元(比如方法)中獲取程序需要的所有操作數(shù),并為它們分配寄存器——映射到機(jī)器實(shí)際寄存器。真實(shí)程序中,需要的操作數(shù)大于機(jī)器中可用的寄存器數(shù)目。這時(shí)寄存器分配器必須把一些操作數(shù)放到寄存器以外的某個(gè)地方(比如堆棧),也就是說會發(fā)生操作數(shù)溢出。

x86_64 上有16個(gè)通用寄存器(并非每個(gè)寄存器都可用)。目前,大多數(shù)機(jī)器還有16個(gè) AVX 寄存器。發(fā)生溢出時(shí),可以不存儲到堆棧而存儲到 XMM 寄存器中嗎?答案是可以。這么做會帶來什么好處?

4. 實(shí)驗(yàn)

看看下面這個(gè)簡單的 JMH 基準(zhǔn)測試,用一種非常特殊的方式構(gòu)建基準(zhǔn)(簡單起見,這里假設(shè) Java 具備有預(yù)處理能力):

import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class FPUSpills {
    int s00, s01, s02, s03, s04, s05, s06, s07, s08, s09;
    int s10, s11, s12, s13, s14, s15, s16, s17, s18, s19;
    int s20, s21, s22, s23, s24;
    int d00, d01, d02, d03, d04, d05, d06, d07, d08, d09;
    int d10, d11, d12, d13, d14, d15, d16, d17, d18, d19;
    int d20, d21, d22, d23, d24;
    int sg;
    volatile int vsg;
    int dg;
    @Benchmark
#ifdef ORDERED
    public void ordered() {
#else
    public void unordered() {
#endif
        int v00 = s00; int v01 = s01; int v02 = s02; int v03 = s03; int v04 = s04;
        int v05 = s05; int v06 = s06; int v07 = s07; int v08 = s08; int v09 = s09;
        int v10 = s10; int v11 = s11; int v12 = s12; int v13 = s13; int v14 = s14;
        int v15 = s15; int v16 = s16; int v17 = s17; int v18 = s18; int v19 = s19;
        int v20 = s20; int v21 = s21; int v22 = s22; int v23 = s23; int v24 = s24;
#ifdef ORDERED
        dg = vsg; // 給 optimizer 制造點(diǎn)麻煩
#else
        dg = sg;  // 只做常規(guī)存儲
#endif
        d00 = v00; d01 = v01; d02 = v02; d03 = v03; d04 = v04;
        d05 = v05; d06 = v06; d07 = v07; d08 = v08; d09 = v09;
        d10 = v10; d11 = v11; d12 = v12; d13 = v13; d14 = v14;
        d15 = v15; d16 = v16; d17 = v17; d18 = v18; d19 = v19;
        d20 = v20; d21 = v21; d22 = v22; d23 = v23; d24 = v24;
    }
}

上面的例子中一次會讀寫多對字段。實(shí)際上,優(yōu)化器本身并不會與具體程序綁定。事實(shí)上,這就是在 unordered 測試中觀察到的結(jié)果:

Benchmark                                  Mode  Cnt   Score    Error  Units
FPUSpills.unordered                        avgt   15   6.961 ±  0.002  ns/op
FPUSpills.unordered:CPI                    avgt    3   0.458 ±  0.024   #/op
FPUSpills.unordered:L1-dcache-loads        avgt    3  28.057 ±  0.730   #/op
FPUSpills.unordered:L1-dcache-stores       avgt    3  26.082 ±  1.235   #/op
FPUSpills.unordered:cycles                 avgt    3  26.165 ±  1.575   #/op
FPUSpills.unordered:instructions           avgt    3  57.099 ±  0.971   #/op

上面展示了26對 load-store,實(shí)際測試中大致有25對,但是這里沒有25個(gè)通用寄存器!從 perfasm 結(jié)果中可以看到,優(yōu)化器會把臨近的 load-store 對合并,減小寄存器壓力:

0.38%    0.28%  ↗  movzbl 0x94(%rcx),%r9d
                │  ...
0.25%    0.20%  │  mov    0xc(%r11),%r10d    ; 讀取字段 s00
0.04%    0.02%  │  mov    %r10d,0x70(%r8)    ; 存儲字段 d00
                │  ...
                │  ... (transfer repeats for multiple vars) ...
                │  ...
                ╰  je     BACK

ordered 測試會給優(yōu)化器制造一點(diǎn)混亂,在存儲前全部加載。上面的結(jié)果也印證了這一點(diǎn):先全部加載,再全部存儲。加載全部完成時(shí)寄存器的壓力最大,這時(shí)還沒有開始存儲。即便如此,從結(jié)果來看與 unordered 差異不大:

Benchmark                                  Mode  Cnt   Score    Error  Units
FPUSpills.unordered                        avgt   15   6.961 ±  0.002  ns/op
FPUSpills.unordered:CPI                    avgt    3   0.458 ±  0.024   #/op
FPUSpills.unordered:L1-dcache-loads        avgt    3  28.057 ±  0.730   #/op
FPUSpills.unordered:L1-dcache-stores       avgt    3  26.082 ±  1.235   #/op
FPUSpills.unordered:cycles                 avgt    3  26.165 ±  1.575   #/op
FPUSpills.unordered:instructions           avgt    3  57.099 ±  0.971   #/op
FPUSpills.ordered                          avgt   15   7.961 ±  0.008  ns/op
FPUSpills.ordered:CPI                      avgt    3   0.329 ±  0.026   #/op
FPUSpills.ordered:L1-dcache-loads          avgt    3  29.070 ±  1.361   #/op
FPUSpills.ordered:L1-dcache-stores         avgt    3  26.131 ±  2.243   #/op
FPUSpills.ordered:cycles                   avgt    3  30.065 ±  0.821   #/op
FPUSpills.ordered:instructions             avgt    3  91.449 ±  4.839   #/op

這是因?yàn)橐呀?jīng)設(shè)法把操作數(shù)溢出到 XMM 寄存器中,而不是在堆棧上存儲:

3.08%    3.79%  ↗  vmovq  %xmm0,%r11
                │  ...
0.25%    0.20%  │  mov    0xc(%r11),%r10d    ; 讀取字段 s00
0.02%           │  vmovd  %r10d,%xmm4        ; < --- FPU 溢出
0.25%    0.20%  │  mov    0x10(%r11),%r10d   ; 讀取字段 s01
0.02%           │  vmovd  %r10d,%xmm5        ; < --- FPU 溢出
                │  ...
                │  ... (讀取更多字段和 XMM 溢出) ...
                │  ...
0.12%    0.02%  │  mov    0x60(%r10),%r13d   ; 讀取字段 s21
                │  ...
                │  ... (讀取到寄存器) ...
                │  ...
                │  ------- 讀取完成, 開始寫操作 ------
0.18%    0.16%  │  mov    %r13d,0xc4(%rdi)   ; 存儲字段 d21
                │  ...
                │  ... (讀寄存器并存儲字段)
                │  ...
2.77%    3.10%  │  vmovd  %xmm5,%r11d        : < --- FPU 取消溢出
0.02%           │  mov    %r11d,0x78(%rdi)   ; 存儲字段 d01
2.13%    2.34%  │  vmovd  %xmm4,%r11d        ; < --- FPU 取消溢出
0.02%           │  mov    %r11d,0x70(%rdi)   ; 存儲字段 d00
                │  ...
                │  ... (取消溢出并存儲字段)
                │  ...
                ╰  je     BACK

請注意:這里的確對某些操作數(shù)使用了通用寄存器(GPR),但是當(dāng)所有寄存器被用完時(shí)會發(fā)生溢出。這里對時(shí)機(jī)的描述并不確切??雌饋硐劝l(fā)生了溢出,然后使用 GPR。然而這是一個(gè)假象,因?yàn)榧拇嫫鞣峙淦魇窃谌诌M(jìn)行分配。

(2) 一些寄存器分配器實(shí)際執(zhí)行的是線性分配,提高了 regalloc 的速度與生成代碼的效率。

XMM 溢出延遲似乎是最小的:盡管溢出需要更多指令,但它們的執(zhí)行效率很高能夠有效彌補(bǔ)流水線的缺陷。通過34條額外指令,大約17條溢出指令對,實(shí)際只要求4個(gè)額外周期。請注意,按照 4/34 = ~0.11 時(shí)鐘/指令 計(jì)算 CPI 是不對的,計(jì)算結(jié)果會超出當(dāng)前 CPU 處理能力。但是實(shí)際帶來的改進(jìn)是真實(shí)的,因?yàn)槭褂昧艘郧皼]有用到的執(zhí)行塊。

沒有參照談效率是毫無意義的。這里用 -XX:-UseFPUForSpilling 讓 Hotspot 禁用 FPU 溢出,這樣可以了解 XMM 溢出帶來的好處:

Benchmark                                  Mode  Cnt   Score    Error  Units
# Default
FPUSpills.ordered                          avgt   15   7.961 ±  0.008  ns/op
FPUSpills.ordered:CPI                      avgt    3   0.329 ±  0.026   #/op
FPUSpills.ordered:L1-dcache-loads          avgt    3  29.070 ±  1.361   #/op
FPUSpills.ordered:L1-dcache-stores         avgt    3  26.131 ±  2.243   #/op
FPUSpills.ordered:cycles                   avgt    3  30.065 ±  0.821   #/op
FPUSpills.ordered:instructions             avgt    3  91.449 ±  4.839   #/op
# -XX:-UseFPUForSpilling
FPUSpills.ordered                          avgt   15  10.976 ±  0.003  ns/op
FPUSpills.ordered:CPI                      avgt    3   0.455 ±  0.053   #/op
FPUSpills.ordered:L1-dcache-loads          avgt    3  47.327 ±  5.113   #/op
FPUSpills.ordered:L1-dcache-stores         avgt    3  41.078 ±  1.887   #/op
FPUSpills.ordered:cycles                   avgt    3  41.553 ±  2.641   #/op
FPUSpills.ordered:instructions             avgt    3  91.264 ±  7.312   #/op

上面的結(jié)果可以看到 load/store 計(jì)數(shù)增加,為什么?這些是堆棧溢出。雖然堆棧本身速度很快,但仍然在內(nèi)存中運(yùn)行,訪問 L1 緩存中的堆??臻g?;旧洗蠹s需要額外17個(gè)存儲對,但現(xiàn)在只需要約11個(gè)時(shí)鐘周期。這里 L1 緩存的吞吐量是主要限制。

最后,可以觀察 -XX:-UseFPUForSpilling 的 perfasm 輸出:

2.45%    1.21%  ↗  mov    0x70(%rsp),%r11
                │  ...
0.50%    0.31%  │  mov    0xc(%r11),%r10d    ; 讀取字段 s00
0.02%           │  mov    %r10d,0x10(%rsp)   ; < --- 堆棧溢出!
2.04%    1.29%  │  mov    0x10(%r11),%r10d   ; 讀取字段 s01
                │  mov    %r10d,0x14(%rsp)   ; < --- 堆棧溢出!
                │  ...
                │  ... (讀取其它字段和堆棧溢出) ...
                │  ...
0.12%    0.19%  │  mov    0x64(%r10),%ebp    ; 讀取字段 s22
                │  ...
                │  ... (more reads into registers) ...
                │  ...
                │  ------- 讀取完成, 開始寫操作 ------
3.47%    4.45%  │  mov    %ebp,0xc8(%rdi)    ; 存儲字段 d22
                │  ...
                │  ... (讀取更多寄存器和存儲字段)
                │  ...
1.81%    2.68%  │  mov    0x14(%rsp),%r10d   ; < --- 取消堆棧溢出
0.29%    0.13%  │  mov    %r10d,0x78(%rdi)   ; 存儲字段 d01
2.10%    2.12%  │  mov    0x10(%rsp),%r10d   ; < --- 取消堆棧溢出
                │  mov    %r10d,0x70(%rdi)   ; 存儲字段 d00
                │  ...
                │  ... (取消其它溢出和存儲字段)
                │  ...
                ╰  je     BACK

的確,在堆棧溢出發(fā)生的地方也可以看到 XMM 溢出。

5. 觀察

FPU 溢出是緩解寄存器壓力的一種好辦法。雖然不增加通用寄存器寄存器數(shù)量,但確實(shí)在溢出時(shí)提供了更快的臨時(shí)存儲。在僅需要幾個(gè)額外的溢出存儲時(shí),可以避免轉(zhuǎn)存到 L1 緩存支持的堆棧。

這為什么有時(shí)會出現(xiàn)奇怪的性能差異:如果在一些關(guān)鍵路徑上沒有用到 FPU 溢出,很可能會看到性能下降。例如,引入一個(gè) slow-path GC 屏障,假定會清除 FPU 寄存器,可能會讓編譯器回退到堆棧溢出,并不去嘗試其它優(yōu)化。

對支持 SSE 的 x86 平臺、ARMv7 和 AArch64,Hotspot 默認(rèn)啟用 -XX:+UseFPUForSpilling。因此,無論是否知道這個(gè)技巧,大多數(shù)程序都能從中受益。

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

    關(guān)注

    134

    文章

    9345

    瀏覽量

    376456
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5432

    瀏覽量

    124066
  • 存儲器
    +關(guān)注

    關(guān)注

    38

    文章

    7644

    瀏覽量

    166969
  • JVM
    JVM
    +關(guān)注

    關(guān)注

    0

    文章

    160

    瀏覽量

    12578
  • FPU
    FPU
    +關(guān)注

    關(guān)注

    0

    文章

    45

    瀏覽量

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

掃碼添加小助手

加入工程師交流群

    評論

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

    x86硬件平臺的最佳伴侶

    的服務(wù)領(lǐng)域,這我們能夠得到的性價(jià)比最高的計(jì)算機(jī),在業(yè)界的霸主地位無可替代。但x86平臺的一個(gè)小小的瑕疵給希望pc工作高可靠場合的用戶帶來了不少的麻煩,即絕大部分
    發(fā)表于 08-29 10:26

    X86硬件設(shè)計(jì)系列知識分享

    論壇有很多嵌入式的技術(shù)資料,卻幾乎沒有X86硬件(PC,Server 等等)設(shè)計(jì)技術(shù)資料。本人從事X86硬件及系統(tǒng)設(shè)計(jì)多年,總結(jié)了系列X86平臺設(shè)計(jì)知識,逐步分享出來,期望能夠?qū)氖孪?/div>
    發(fā)表于 10-17 12:18

    解讀x86、ARM和MIPS三種主流芯片架構(gòu)

    代碼記憶體里執(zhí)行,新設(shè)計(jì)的處理,只需增加較少的電晶體就可以執(zhí)行同樣的指令集,也可以很快地編寫新的指令集程式;二是擁有龐大的指令集,x86擁有包括雙運(yùn)算元格式、寄存器
    發(fā)表于 05-25 16:09

    x86平臺架構(gòu)如何為用戶帶來豐富的交互式駕駛體驗(yàn)?

    本文主要講述了x86平臺架構(gòu)如何為用戶帶來豐富的交互式駕駛體驗(yàn),而這是非PC兼容型平臺難以實(shí)現(xiàn)的。
    發(fā)表于 05-14 06:45

    嵌入式X86和ARM各自都有哪些優(yōu)缺點(diǎn)呢

    代碼記憶體里執(zhí)行;二是擁有龐大的指令集,擁有包括雙運(yùn)算元格式、寄存器寄存器寄存器到記憶體以及記憶體到寄存器的多種指令類型。
    發(fā)表于 12-14 09:21

    TarsARM平臺上的移植是如何去實(shí)現(xiàn)的

    計(jì)時(shí)來實(shí)現(xiàn),具體實(shí)現(xiàn)如下。原x86嵌匯編實(shí)現(xiàn):支持ARM64平臺后的實(shí)現(xiàn):3 協(xié)程實(shí)現(xiàn)協(xié)程是一種用戶態(tài)的輕量級線程,其調(diào)度完全由用戶控制。因此,協(xié)程調(diào)度切換時(shí)需要用戶自己將寄存器和棧
    發(fā)表于 03-30 11:30

    arm64和x86服務(wù)上運(yùn)行的耗時(shí)來發(fā)現(xiàn)Dockerarm64架構(gòu)下的性能問題

    發(fā)現(xiàn)Dockerarm64架構(gòu)下的性能問題。本文描述的性能測試是分別在一arm64和一x86服務(wù)上進(jìn)行的,兩個(gè)服務(wù)
    發(fā)表于 07-12 15:48

    英特爾雙核處理 進(jìn)軍x86平臺市場

        英特爾(Intel)于15日宣布Core Duo處理(Yonah雙核心處理)進(jìn)軍x86嵌入式平臺市場,
    發(fā)表于 03-13 13:00 ?637次閱讀

    LinuxBIOS嵌入式x86系統(tǒng)中的配置

    LinuxBIOS嵌入式x86系統(tǒng)中的配置   引言   嵌入式計(jì)算機(jī)系統(tǒng)隨著其應(yīng)用方式不同,具有不同的體系結(jié)構(gòu)形式?;?b class='flag-5'>x86平臺的嵌入式計(jì)算機(jī)
    發(fā)表于 12-11 10:00 ?999次閱讀

    X86平臺嵌入式軟件應(yīng)用

    X86平臺嵌入式軟件應(yīng)用
    發(fā)表于 01-14 02:36 ?0次下載

    如何在C代碼中插入寄存器

    寄存。這起到了隔離關(guān)鍵路徑的作用。 但是,如果使用的RTL代碼是HLS轉(zhuǎn)換生成的,例如使用Vitis HLS綜合的,其可讀性較差,想要在其生成的HDL
    的頭像 發(fā)表于 02-02 17:07 ?3395次閱讀
    如何在C<b class='flag-5'>代碼</b>中插入<b class='flag-5'>寄存器</b>?

    微機(jī)原理筆記——x86寄存器

    8086 CPU中寄存器總共為14個(gè),且均為16位。即 AX,BX,CX,DX,SP,BP,SI,DI,IP,F(xiàn)LAG,CS,DS,SS,ES 共 14 個(gè)。而這 14 個(gè)寄存器按照一定方式又分
    發(fā)表于 12-08 18:21 ?7次下載
    微機(jī)原理筆記——<b class='flag-5'>x86</b><b class='flag-5'>寄存器</b>

    基于X86平臺的ARM指令集模擬設(shè)計(jì)

    電子發(fā)燒友網(wǎng)站提供《基于X86平臺的ARM指令集模擬設(shè)計(jì).pdf》資料免費(fèi)下載
    發(fā)表于 10-11 15:03 ?0次下載
    基于<b class='flag-5'>X86</b><b class='flag-5'>平臺</b>的ARM指令集模擬<b class='flag-5'>器</b>設(shè)計(jì)

    基于X86平臺的ARM指令集模擬的設(shè)計(jì)方案

    電子發(fā)燒友網(wǎng)站提供《基于X86平臺的ARM指令集模擬的設(shè)計(jì)方案.pdf》資料免費(fèi)下載
    發(fā)表于 11-06 11:54 ?0次下載
    基于<b class='flag-5'>X86</b><b class='flag-5'>平臺</b>的ARM指令集模擬<b class='flag-5'>器</b>的設(shè)計(jì)方案

    觸翔X86工控主板交互機(jī)器人中的應(yīng)用

    X86工控主板交互機(jī)器人中的應(yīng)用主要體現(xiàn)在其高可靠性、抗干擾性和穩(wěn)定性等方面,為交互機(jī)器人的正常運(yùn)行和精確控制提供了有力支持。以下是關(guān)于X86
    的頭像 發(fā)表于 05-28 15:48 ?751次閱讀