<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Arm Neoverse N1 Core: 性能分析方法

      1 引言

      隨著許多 Arm 硬件和軟件合作伙伴開發應用程序并將其工作負載移植到基于 Arm 的云實例上,Arm Neoverse 生態系統正在大幅增長。隨著基于 Neoverse N1 的系統廣泛普及,許多實際工作負載顯示出了與傳統系統相比極具競爭力的性能和顯著的成本節約。最近的一些例子包括 H.264 視頻編碼、memcached、Elasticsearch、NGINX 等。

      為了最大限度地提高執行性能,開發人員使用性能分析和工作負載特征描述技術來研究應用程序的性能特征。服務器類系統支持多種性能監控技術,可用于測量工作負載效率、評估資源需求和跟蹤資源利用情況。此類測量有助于調整軟件和硬件,也有助于指導未來的系統設計要求。

      Arm Neoverse 微架構的開發需要考慮到高性能和低功耗。因此,我們的性能監控理念與軟件開發人員分析基于其他架構的系統時所采用的理念略有不同。本文概述了利用 Neoverse N1 CPU 上的性能監控單元 (PMU Performance Monitoring Unit) 功能進行工作負載鑒定的方法,以識別和消除性能瓶頸。目標讀者是從事軟件優化、調優和開發工作的軟件開發人員和性能分析人員。

      本文內容分為四章:

      • 第一章介紹了 NeoverseN1 上的硬件 PMU,以及與工作負載特征描述最相關的 PMU 事件。
      • 第二章介紹使用 Neoverse N1 內核 PMU 事件進行工作負載鑒定的方法。
      • 第三章說明了如何使用 Linux perf 工具收集 Neoverse N1 PMU 事件。
      • 最后一章通過一個工作負載案例研究,展示了工作負載特征描述和熱點分析。

      2 Neoverse N1 性能監控事件

      性能監控需要收集特定系統上的應用執行信息,這些信息可以通過軟件或硬件手段獲得。軟件監控技術提供軟件跟蹤和系統軟件統計的事件。硬件監控技術則直接從 CPU/系統中收集硬件事件。為了收集硬件事件,現代處理器采用了專用的性能監控單元(PMU),便于測量各種硬件執行相關事件??梢员O控多個事件,并將其與軟件執行相關聯,以確定優化機會,評估工作負載是否以最佳方式利用了底層微體系結構。支持的部分事件包括指令退役(退休)、CPU 運行周期、緩存/TLB 訪問和分支預測。

      2.1 Arm 性能監控單元

      Arm 架構通過名為性能監控器擴展(PES: Performance Monitors Extension)的可選擴展支持 PMU 功能。Arm PMU 硬件設計由以下組件組成:

      • 用于控制和事件選擇的 PMU 配置寄存器
      • PMU 事件計數器
      • 專用功能計數器

      ArmPMU 硬件采用多個配置寄存器,包括 PMCR 和 PMEVTYPER 寄存器,分別用于單元控制和測量選擇。PMU 硬件還包含一組事件計數器,用于根據用戶要求對原始硬件事件進行計數。每個事件都有一個由硬件供應商設計的與之相關的十六進制事件代碼,并將其設置在 PMEVTYPER(Performance Monitor Event Type Register) 寄存器中。除了可配置計數器外,AArch64 還為 CPU cycle提供了專用計數器,這是所有 Arm 兼容設計所必需的功能。PMU 硬件的實現與可用計數器的數量和可計數的事件類型(包括相應的事件代碼)有關。

      當配置 PMU 事件時,其中一個事件計數器將被分配到與被測事件相關的數字邏輯上。PMU 事件可使用 Linux perf 工具測量,既可使用軟件列出的常見 “硬件事件 ”的事件名稱,也可使用與架構指定的 “PMU 硬件事件 ”相關的專用事件代碼。典型的處理器可能支持數百個用于性能和調試目的的事件,但可以選擇一個子集進行工作負載特征描述,以研究高級執行瓶頸和資源利用情況。針對子系統單元的其他特定事件可用于深入分析性能問題,以及從特性分析中確定的性能限制單元。

      2.1.1 Arm PMU 實現參考

      這些事件的行為在各自的《Arm 架構參考手冊 》中被定義為 “通用事件”。通用事件 "是通用定義,適用于所有微體系結構,但大部分是軟件需求,不一定能實現。

      2.2 Neoverse N1 性能監控單元

      Neoverse N1 CPU 實現了 Arm v81 的 PMU 擴展,支持 100 多個硬件事件。Neoverse N1 PMU具有6個可配置計數器和1個專用計數CPU cycle的功能計數器。
      Neoverse N1 內核實現的 PMU 事件已在 ARM Neoverse N1 內核技術參考手冊(TRM)D2 部分中列出。除 TRM 外,我們還提供了 Neoverse N1 PMU 指南,這是有關內核實現的硬件 PMU 事件的補充指南。該 PMU 指南詳細描述了按 CPU 模塊分類的 PMU 事件。

      為了更好地理解每個 PMU 事件,本指南還包括了微體系結構和體系結構所需的定義,并在每個 PMU 事件的描述中標注了相關定義作為參考。
      我們建議將《N1 PMU 指南》作為使用 PMU 事件進行性能分析活動的事件描述參考手冊。

      2.3 Neoverse N1 Core PMU 工作負載特征描述事件表

      盡管Neoverse N1 Core支持 100 多個硬件計數器,但并非所有計數器都需要用于工作負載執行的初始特征描述。下圖是用于 Neoverse N1 CPU 首次工作負載特性分析的主要性能監控事件清單。

      3 Neoverse N1 性能分析方法

      如今,性能優化和軟件調整比以往任何時候都更具挑戰性?,F代高端處理器通常具有高內核數、復雜指令集、不同程度的并行性和深層內存層次結構。此外,大規模云工作負載中有很大一部分是在虛擬機上運行的,這使得追蹤工作負載的執行行為以評估數據中心的電源性能效率變得更加困難。因此將軟件執行與微體系結構行為關聯起來,對于優化軟件在底層硬件上的高效執行至關重要。

      單核構成了處理器的基本執行塊。這些內核通過多個配置和子系統組合在一起,形成一個計算系統。在本章中,我們將探討單個 Neoverse N1 內核的高級細節,以及使用內核的硬件 PMU 事件進行工作負載鑒定的方法。

      3.1 Neoverse N1 Core

      Neoverse N1 Core是一個無序超級標量機,每個周期最多可分派/退役 8 條指令。超級標量處理器的流水線有三個主要階段:

      • 指令流的按序取指和解碼稱為前端
      • 無序執行部分稱為后端。
      • 有序提交/退休

      CPU有一個內存子系統,負責所有內存操作及其有序執行。所有這些主要 CPU 模塊的設計細節因微體系結構而異。

      在本文檔中,我們將介紹大多數超標量架構中常見的模塊,并強調適用于 N1 實現的相關性能指標。有關 Neoverse N1 微體系結構的詳細說明,請參閱《Neoverse N1 技術參考手冊》和《Neoverse 軟件優化手冊》。下圖顯示了 Neoverse N1 CPU 的高級框圖。

      3.1.1 前端

      CPU 的前端是一條按順序排列的流水線,用于處理從 I-Cache 抓取指令、對這些指令進行解碼以及為后端執行引擎排隊。在解碼階段,架構指令可分解為微操作。這些微操作根據其可用性排隊并分派給執行引擎。除了獲取、解碼和調度單元外,還有一個重命名塊,用于跟蹤操作數據流和依賴關系,確保已執行的操作按順序提交。前端的另一個重要單元是分支預測器。該單元可預測分支的方向以及間接分支的目標地址。分支預測技術有助于盡可能按照正確的程序順序獲取指令,因為分支錯誤預測會導致管道刷新和周期浪費。Neoverse N1 每個周期最多可獲取 4 條指令,最多可進行 8 次微操作。
      操作。

      3.1.2 后端

      CPU 的后端處理微操作的執行,這些微操作被分派到相關執行單元進行處理。Neoverse N1 支持多個執行單元,包括分支單元、加載/存儲單元(load/store)和算術單元(包括高級矢量引擎 (advanced vectorengines))。執行單元的數量和執行一條指令所需的周期因微體系結構而異;因此,指令執行的延遲和吞吐量取決于實施情況。指令執行完成后,會按順序存儲并提交相關結果。

      Neoverse N1 有 4 個整數執行單元、2 條浮點/SIMD 流水線和 2 條加載/存儲流水線,因此每個周期最多可向執行流水線發送 8 個微操作。

      3.1.3 內存子系統

      CPU 的內存子系統處理加載和存儲操作的執行,這在很大程度上依賴于內存的層次結構。Neoverse N1 每個內核都有一個專用的 L1/L2 高速緩存,其中 L2 高速緩存由 L1 數據高速緩存和 L1 指令高速緩存共享。加載存儲單元控制高速緩存和內存之間的數據流。

      Neoverse N1 有兩個加載/存儲單元,可同時處理讀寫操作。L1 數據高速緩存是一個 64kB 的 4 路關聯集設計,L2 高速緩存是一個 8 路關聯集高速緩存,最大容量為 1MB,可根據實現情況進行配置。內核的專用二級緩存通過 AMBA 5 CHI 接口連接到系統的其他部分。

      • Neoverse N1 群集配置

      Neoverse N1 系統有不同的配置,具體取決于互連和群集/系統級末級高速緩存的實施選擇。圖4和圖5顯示了 NeoverseN1 系統中的一些可選配置。圖 4 顯示了一種配置,在這種配置中,多個內核可以配置為基于動態共享單元(DSU)的集群系統,單個集群中包含兩個內核。該 DSU 群集包含一個可選的 L3 高速緩存,可由群集內的內核共享。這種可選的 L3 高速緩存在設計上最大可達 2 MB。

      另一種配置是圖 5 所示的直接連接系統,顧名思義,內核直接連接到稱為 CAL 的相干網格互連接口。這些系統不支持 L3 高速緩存,因為沒有 DSU 集群。

      所有采用相干網狀互連的系統都支持共享的系統級高速緩存,其大小可達 256MB。了解所分析系統的高速緩存結構和配置,對于從高速緩存有效性 PMU 事件中獲得洞察力至關重要。最好向提供商了解包括緩存大小在內的底層系統的系統配置詳情。

      3.2 性能分析方法

      對于工作負載分析,原始硬件事件以及從中得出的一些有用指標都可用于鑒定。要理解所有事件并決定使用哪些事件并非易事,因為每個工作負載都有獨特的行為和潛在瓶頸。為了簡化這一過程,我們將重點介紹一些有助于對工作負載進行初步描述的原始事件和衍生指標。遵循這一流程將有助于制定工作負載的頂層特征,并從根本上解決深度挖掘流程中的性能問題。

      可遵循的基本頂層分析方法首先是計算執行周期的使用情況,如圖 6 所示。

      3.2.1 IPC(Instructions Per Cycle)每周期指令數

      消耗的cycle數和執行的指令數量為任何體系結構上的 CPU 工作負載和執行時間提供了概況。指令數量代表 CPU 所做的工作量,而cycle代表完成這些工作所需的總時間。每周期指令數(IPC)是評估微體系結構工作負載性能的一個關鍵指標,其推導過程如下:

      在非停頓(non-stalled)流水線中,IPC 將是最高的,可以直接監測處理器支持的指令級并行性。IPC 達到的越高,執行過程中流水線的效率就越好。如果 IPC 非常低,這意味著花費的cycle大量停頓,這可能導致潛在的性能問題。Neoverse N1 流水線的最大 IPC 為 4,因為它每個周期可以獲取 4 條指令。
      在指令計數方面,Neoverse N1 支持 INST_RETIRED 和 INST_SPEC 事件,這兩種事件都計算已執行的指令,但處于流水線的不同階段。
      INST_RETIRED 計算的是程序中按架構執行的指令總數,而 INST_SPEC 計算的是向處理器推測解碼的指令總數。INST_SPEC 可以更好地說明執行單元的總利用率,因為它計算的是本可以執行但不一定提交的指令。INST_SPEC 和 INST_RETIRED 之間的巨大差異通常表明分支錯誤預測率很高,或者其他故障會取消已編碼的投機指令,這是不高效的。

      請注意,工作負載的 INST_RETIRED 在架構的所有實現上都是相同的,而周期則因微架構實現和系統配置(是否連接到云)而異。

      3.2.2 周期計數/流水線停頓(Cycle Accounting/Pipeline Stalls)

      無序CPU有一個有序前端單元,負責獲取指令并將其解碼為微操作,然后下發到后端執行。前端的取指單元從 L1I 高速緩存中獲取指令,這需要訪問 L1I TLB (一級指令轉換后備緩沖器 Level 1 Instruction Translation Lookaside Buffer)以獲取物理地址,還需要一個分支預測單元來推測性地獲取后續指令。雖然后端可以不按順序執行微操作,但所有指令都是按順序執行的。

      上述流水線可實現高吞吐量的指令執行。但是,流水線中可能會出現停頓,原因有很多,例如由于分支預測錯誤或等待內存或 L2/L3 高速緩存中的數據而獲取錯誤的代碼路徑。此外,執行過多的錯誤路徑代碼會減少 CPU 的有用周期數。為了評估管道執行的效率,NeoverseN1 支持多級 STALL 事件,以評估 CPU 前端和后端停滯的周期數。

      STALL_FRONTEND 和 STALL_BACKEND 事件可用于顯示工作負載執行過程中的主要瓶頸。利用這兩個事件,我們可以分別得出前端和后端停滯周期的相對百分比:

      相對較高的前端停滯率表明,周期浪費是由于前端內序分部的流水線停滯造成的,而相對較高的后端停滯率表明,周期浪費是由于后端的流水線停滯造成的。這種細分有助于縮小主要區塊的范圍,進一步分析這些區塊以確定性能瓶頸。

      以下是為進一步分解前端停頓工作負載而分析的 CPU 塊/單元列表:

      • ITLB 事件
      • I-Cache 事件: L1I + L2/Last 級緩存事件
      • 分支效率事件(Branch Effectiveness events)

      以下是為進一步分解后端停頓工作負載而分析的 CPU 塊/單元列表:

      • DTLB(數據轉換后備緩沖器:Data Translation Lookaside Buffer)事件
      • 內存系統相關事件
      • D-Cache緩存計數器: L1D + L2/末級緩存事件
      • 指令混合

      N1 PMU 指南詳細介紹了可用于上述各 CPU 塊的所有原始硬件事件。接下來,我們將介紹 N1 PMU CheatSheet[第 2 章]中推薦的部分事件,包括可用于描述 CPU 塊特性的一些指標。

      3.2.3 分支有效性(Branch Effectiveness)

      分支調度預測在流水線化的 CPU 中成本很高,會導致頻繁的流水線刷新和周期浪費。一般來說,工作負載通常平均每 6 條指令中包含 1 個分支。雖然現代 CPU 擁有優化的分支預測單元,但仍有許多使用案例,如光線跟蹤、決策樹算法等,分支繁多且難以預測。在其中一些應用中,可能會有數百條獨特的分支路徑,而且目標可能與輸入數據有關。

      分支預測性能可通過兩個原始 PMU 事件 BR_MIS_PRED_RETIRED 和 BR_RETIRED 進行評估。BR_MIS_PRED_RETIRED 顯示了已執行但被錯誤預測的分支總數。這意味著錯誤預測的基本代碼塊中的代碼路徑方向是錯誤的,路徑中的后續操作被浪費了,導致管道堵塞。

      可以得出兩個性能指標,用于高水平評估分支執行性能與整個程序執行的關系:

      分支 MPKI 提供每千條指令錯誤預測的分支總數,而分支錯誤預測率則顯示錯誤預測的分支與整體分支的比率。這兩個指標都可用于使用 perf 記錄進行進一步調查,以確定是哪些函數導致了分支未命中率的增加 [第 4 章]。

      3.2.4 分支混合/預測性能(Branch Mix/Prediction Performance)

      分支預測單元根據分支類型以不同方式工作。主要有三個部分

      • 分支歷史表 (BHT Branch History Table),用于存儲已執行或未執行的條件分支的歷史記錄。
      • 分支目標緩沖區(BTB Branch Target Buffer),用于存儲間接分支的目標地址
      • 返回地址棧(RAS Return Address Stack),用于存儲函數返回分支。

      Neoverse N1 支持三個事件:BR_IMD_SPEC、BR_RETURN_SPEC 和 BR_INDIRECT_SPEC,分別用于對執行的立即、間接和返回分支進行分類。對分支類型進行細分有助于深入了解分支預測單元中每個子模塊的性能。請注意,這些事件同時計算正確預測和錯誤預測的分支。不支持只計算錯誤預測分支的相應事件。在 Neoverse N1 上,Statistical ProfilingExtensions(SPE)可用于將分支預測錯誤歸因于單個分支,從而提供比 PMU 事件更有針對性的分析。

      3.2.5 TLB/MMU 效能(TLB/MMU Effectiveness)

      另一個重要的性能評估步驟是檢查虛擬內存系統的性能,它影響前端的指令取回性能和數據端的內存訪問性能。處理器在訪問相應的高速緩存之前,需要將任何指令/數據內存訪問的虛擬地址轉換為物理地址。請注意,程序對內存的看法是虛擬地址,但處理器在訪問高速緩存或內存時使用的是物理地址。

      虛擬地址和物理地址在系統內存中的頁轉換表中定義。訪問這些表需要一次或多次內存訪問,需要多個周期才能完成--這被稱為頁表走行。不過,為了加快翻譯速度,翻譯查找邊緩沖區(TLB Translation Lookaside Buffers)會緩存翻譯表的走行,從而大大減少對系統內存的訪問次數。

      第一層包含用于指令和數據(加載/存儲)地址轉換的獨立專用 TLB。對這些 TLB 的總訪問量分別由 L1I_TLB 和 L1D_TLB 計算。第二層包含一個統一的 L2TLB,由 I 側和 D 側訪問共享。有相應的 REFILL 計數器,對這些 TLB 層中的填充進行計數。由于 I 側和 D 側 TLB 中的未命中而導致頁表走行的訪問分別通過 ITLB_WALK 和 DTLB_WALK 事件進行計數。

      為了評估 TLB 的有效性,可以從原始事件中得出四個指標:

      ITLB MPKI 和 DTLB MPKI 分別提供了指令和數據訪問中每千條指令的 TLB 走行率。DTLBWalkRate 提供了 DTLBWalks 與程序總體 TLB 查找次數的比率。請注意,這與 DTLB_WALK/ MEM_ACCESS 相同。MEM_ACCESS 相同,因為每次 MEM_ACCESS 都會導致一次 L1D_TLB 訪問。ITLB 走行率提供了 ITLB 走行占指令端啟動的 TLB 查找總數的百分比。

      3.2.6 指令混合(Instruction Mix)

      NeoverseN1 微體系結構有 8 個執行單元,可以處理五種類型的操作:分支、單周期整數、多周期整數、帶地址生成功能的加載/存儲單元以及高級浮點/SIMD 操作:

      • LD_SPEC: 已發出的加載指令
      • ST_SPEC: 已發出的存儲指令
      • ASE_SPEC: 已發出的高級 SIMD 指令
      • VFP_SPEC: 已發出的浮點指令
      • DP_SPEC:已發出的整數數據處理指令
      • BR_IMD_SPEC:已發出的立即分支指令
      • BR_INDIRECT_SPEC:已發出的間接分支指令
      • BR_RETURN_SPEC:已發出的返回分支指令

      請注意,這些都是推測執行的計數,因為這些指令是在發出階段計數的,可以估算執行單元的利用率,但不能估算程序的退役指令組合。Neoverse N1 不支持用于計算架構指令組合的退役事件計數器。Neoverse N1 支持通過事件 BR_IMD_SPEC、BR_INDIRECT_SPEC 和 BR_RETURN_SPEC 將分支操作進一步細分為立即分支、間接分支和返回分支。這三個分支操作事件的總和可用于計算總分支。要評估 CPU 執行單元的負載,最好是根據 INST_SPEC 計數器(該計數器計算發出的執行指令總數)得出各類操作的百分比。
      舉例說明:

      3.2.7 CPU核心內存流量(Core Memory Traffic)

      MEM_ACCESS事件計算CPU核的Load Store Unit(LSU)發出的內存操作總數。由于這些操作首先在L1D_CACHE中進行查找,因此L1D_CACHE和MEM_ACCESS事件數量相同。Neoverse N1 還支持另外兩個事件 MEM_ACCESS_RD 和 MEM_ACCESS_WR,這兩個事件可以分別提供讀寫流量的分解。請注意,這些事件與LD_SPEC和ST_SPEC不同,因為它們計數issue出去的內存操作,但不一定執行。

      3.2.8 緩存有效性(Cache Effectiveness)

      • 第一級(L1)包括一個專用于指令的高速緩存和一個單獨的專用于數據訪問的高速緩存。
      • 第二級(L2)是統一的二級緩存,代碼和數據共用。
      • 再往下,系統可在內核集群中設置可選的 L3 高速緩存,并在互連中設置可選的共享系統級高速緩存 (SLC)。L3 和 SLC 高速緩存是實施選項。

      Neoverse N1 內核支持所有緩存層次結構級別的分層 PMU 事件。請注意,ARCH64 不支持高速緩存未命中計數器,只支持 REFILL 計數器。高速緩存未命中可能會導致多個高速緩存鏈的填充,如果高速緩存的訪問觸犯了高速緩存鏈的邊界,那么多個高速緩存未命中就會通過一次 REFILL 來滿足。有關高速緩存事件計數器描述的詳細信息,請參閱 N1PMUGuide3。緩存策略和關聯性詳情也可參閱《N1 PMU 指南》中的 “微體系結構 ”一章。

      對于內核的所有高速緩存層次,可以得出一組有用的指標來研究高速緩存行為。例如,L1 數據高速緩存指標可導出如下:

      3.2.9 緩存重填特性分析(Cache REFILL Characterization)

      對于帶有 DSU 集群的 Neoverse N1 系統,N1 還支持兩種高速緩存 REFILL 變體,可用于測量高速緩存是否從集群內部或集群外部填充數據。

      3.2.10 遠程高速緩存訪問(Remote Cache Access)

      對于具有多個套接字或 SOC 的 Neoverse N1 系統,N1 支持 REMOTE_ ACCESS 事件,該事件可統計由來自其他芯片的數據源處理的內存事務。

      3.2.1 末級高速緩存計數器的使用(Last Level Cache Counter Usage)

      正如我們在 [第 3 章] N1 系統配置一節中所看到的,NeoverseN1 系統可以支持集群級 L3 高速緩存和共享的系統級高速緩存。Neoverse N1 為 L3 和 LL(最后一級)實現了兩套緩存分級事件。

      L3 高速緩存是一種可選高速緩存,只有內核實現了 L3 高速緩存,才會計算相應的事件。但是,如果系統配置為雙核集群系統,則此事件可能會計算集群內窺探產生的對等緩存流量。[有關詳細信息,請查閱您的 SOC 規范。]

      在支持共享系統級高速緩存的系統中,LL_CACHE_RD 會統計對 SLC 的總訪問量。在配置了 SLC 以統計 LL_CACHE_RD 事件的系統中,LL_CACHE_RD 計數器會統計內核對 SLC 的總訪問量,而 LL_CACHE_MISS_ RD 會統計在 SLC 上錯過的訪問量。

      為了研究末級讀取行為,可以得出末級緩存讀取未命中指標:

      衡量讀取流量的 SLC 命中率的另一個有用指標是 SLC 讀命中率。在 Neoverse N1 中,末級緩存事件沒有寫入變量,因為 SLC 僅用作內核的驅逐緩存。

      4 使用 Linux Perf 工具進行性能分析

      Linux perf 是一種廣泛使用的開源工具,用于從硬件和系統軟件的不同來源收集軟硬件性能事件。內核采用 perf_event 子系統收集可測量事件,包括來自處理器本身的硬件 PMU 事件。如圖所示,每個內核都有自己專用的 PMU 硬件,內核 perf 驅動程序分別從每個內核 PMU 收集事件。

      Linux perf_event 系統為 Linux 內核和用戶空間性能監控工具提供了一個接口,以便根據需要收集原始硬件事件。Linux perf 工具是 Linux 中的開源工具,可用于性能監控,它支持兩種測量技術:

      • 計數(Counting): 計數收集工作負載執行過程中事件的總體統計數據,其中為每個事件分配的計數器會產生總體事件計數的總和。這種事件統計有助于描述整體工作負載的執行行為,但不提供特定事件在程序中哪個位置發生的任何細節。這種方法是初始工作負載特征描述練習的最佳方法,可用于識別工作負載的性能限制。

      • 基于事件的采樣:事件采樣是一種通過配置PMU 計數器,使其在預先設置的事件數之后溢出,從而對事件進行采樣的方法。有了這些數據,就很容易找到造成大部分采樣事件的庫和代碼部分。

      Linux perf 工具在計數和事件采樣模式下的所有上述性能測量技術都具有以下功能:

      • stat:提供程序整體執行的性能計數器統計數據
      • record:記錄每個庫和函數中每個事件的采樣百分比的執行性能
      • report:使用 record 生成所記錄采樣的報告
      • annotate:在拆解代碼時對報告中的采樣百分比進行注釋

      在需要高精度時,例如在對熱循環或代碼的重要部分進行預處理時,應首選 “計數 ”模式,以確保其準確性--但需要注意的是,在必須對多個不同事件進行記錄時,可能需要進行多次配置文件提取。 對于 Neoverse N1 CPU,最好一次最多計數 6 個事件,以便為每個事件設置一個專用計數器。當事件數多于可用計數器總數時,計數器將在事件之間進行多路復用,并根據總時間段對最終計數進行縮放。這種多路復用計數可能會導致精度問題,但通常是可靠的,除非需要精確測量。

      事件采樣模式對于大部分代碼的熱點分析非常有用,它依賴于統計方法對大部分時間或代碼中的不同事件進行采樣。需要注意的是,這種方法有一些局限性,會導致準確性問題。采樣延遲,即計數器溢出和中斷處理之間的延遲,會導致獲取的數據出現偏差,也就是說,采樣過程中存儲的數據可能不是事件發生的準確點。另一個問題來自于處理器的推測執行方式,如果某些指令在錯誤的代碼路徑上執行并觸發了事件,那么這些指令可能無效。雖然這種方法在準確性上有一定的局限性,但仍然是接近識別代碼執行熱點的最佳方法。
      Linux perf 允許調整采樣頻率,如果數據在不同的運行中顯示出很大的不一致性,這有助于研究事件計數的變化。

      4.1 使用 Linux Perf 工具收集 Arm 架構的硬件 PMU 事件

      為了啟用 PMU 事件收集功能,在構建 Linux 內核時必須在內核配置中啟用 CONFIG_HW_PERF_EVENTS,大多數產品都啟用了此配置選項,但如果您正在編譯自定義內核,請記得啟用此配置選項。此外,還需要以 root 用戶身份配置兩個系統設置,以便獲得內核符號表和額外的特權。

      perf_event_paranoid 控件會影響內核中的權限檢查,將其設置為 -1 會允許打開可能泄露敏感信息或影響系統穩定性的事件。詳情請查看內核文檔。

      kptr_restrict 控件影響內核地址是否公開(例如通過 /proc/kallsyms)。一些開發人員在沒有 vmlinux 的情況下(或在使用 KASLR 的情況下)使用這種技術來獲取內核符號解析。請參閱內核文檔中關于 kptr_restrict 的詳細信息。

      在所有這些情況下,都存在潛在的安全隱患,因此在啟用它們之前,請檢查官方內核文檔并咨詢系統管理員。

      使用 Linux perf 工具的 perf stat 功能來計算指令和周期,是驗證 PMU 事件計數是否正確的簡單測試方法。在 Arm 架構上,0x8 是退役指令的十六進制代碼,0x11 是 CPU 周期的十六進制代碼。
      這些事件提供給 perfstat-e 選項,后綴為 “r”。

      # perf stat -a -e r8,r11 sleep 10
      
       Performance counter stats for 'system wide':
      
         105,767,217,553      r8
          51,378,856,540      r11
      
            10.006758810 seconds time elapsed
      
      

      上述命令將計算 10 秒鐘內所有 CPU 的指令總數和 CPU 周期數。Linux perf 允許對特定 CPU、每個進程、每個線程等進行計數,這可以在 perf stat man 頁面找到。

      請注意,如果某個事件不支持或未啟用,perf 有時會無聲地失敗。請咨詢 CPU TRM,了解系統對事件的支持情況。

      以下章節中的示例使用原始事件編號來說明應監控哪些事件。不過,Linux 內核 4.17 及更高版本支持使用命名值訪問 N1 內核 PMU 事件。對于早期版本,必須使用原始事件編號。有關將命名事件映射到編號的信息,請參閱《Arm Neoverse N1 PMU 指南》。對于 PMU 事件測量的自動化工具,Arm 在 ARM-Software/ PMU-Data 的 Github 存儲庫中提供了包含所有事件和事件代碼的機器可讀 JSON 文件。

      4.2 使用計數模式收集硬件PMU事件

      對于計數模式,請使用“perf stat ”命令,如下所示。

      要統計所有事件以進行特征描述,典型的解決方案是利用平臺中可用的總計數寄存器分批捕獲事件。

      # perf stat -e r8,r11 sleep 10
      
       Performance counter stats for 'sleep 10':
      
                 864,830      r8
               1,461,792      r11
      
            10.001077627 seconds time elapsed
      
             0.001026000 seconds user
             0.000000000 seconds sys
      

      4.3 使用采樣模式收集硬件PMU事件

      對于采樣事件,可以使用Linux Perf工具中的“perf record”命令,如下所示。
      有關如何使用這些命令行進行采樣和分析采樣數據的更多詳細信息,請參閱這些Linux perf示例。

      # perf record -e instructions,cycles -- sleep 5
      # perf report
      # perf annotate
      

      一旦使用計數模式收集事件并按照第3章中概述的方法進行工作負載特征分析,就可以從中篩選出一部分事件進行采樣和熱點分析,如以下例子所示。

      5 Neoverse N1 上的性能分析:案例研究

      在本節中,我們將演示如何使用第 3 章中概述的性能分析方法,通過使用 Linux perf 從 Neoverse N1 系統捕獲的核心 PMU 指標進行工作負載特征描述和熱點分析。我們將介紹一個在 Neoverse N1 軟件開發平臺(N1SDP)上運行的工作負載特性案例研究示例,該平臺有 4 個 Neoverse N1 內核。我們使用 Linux Perf 工具 “perf stat ”一次收集 6 個批次的 PMU 事件。

      5.1 案例研究: DynamoRIO Strided Benchmark

      在案例研究中,我們運行了 DynamoRIO 測試中的 Stride Benchmarkstride 微基準是一個指針追逐基準,它訪問 16MB 數組中的值,數組位置由被追逐的指針決定。指針位置是指針追逐內核運行前在數組中設置的常量值的函數。

      實驗環境1設置

      實驗環境2設置

      # lscpu
      Architecture:           aarch64
        CPU op-mode(s):       64-bit
        Byte Order:           Little Endian
      CPU(s):                 128
        On-line CPU(s) list:  0-127
      Vendor ID:              HiSilicon
        BIOS Vendor ID:       HiSilicon
        Model name:           Kunpeng-920
       ...
      
      # cat /etc/os-release
      NAME="KylinSec OS"
      VERSION="3 (Qomolangma)"
      ID="kylinsecos"
      ID_LIKE="openeuler"
      VERSION_ID="3"
      PRETTY_NAME="KylinSec OS Linux 3 (Qomolangma)"
      ANSI_COLOR="0;31"
      HOME_URL="https://www.kylinsec.com.cn"
      BUG_REPORT_URL="https://support.kylinsec.com.cn"
      
      

      5.1.1 第 1 階段:使用計數模式鑒定工作負載

      IPC 是評估總體工作負載執行效率的第一個嚴格指標。

      觀察說明(表 1): 0.22 的 IPC 遠低于 Neoverse N1 上大多數工作負載的測量值。與之相比,SPEC CPU(r) 201711(估計值)等旨在對系統施加壓力的大型基準工作負載至少能在該 CPU 上實現平均 IPC > 1。在 Neoverse N1 上可實現的最大 IPC 為 4,這通常是由小型和經過大量優化的內核而非大型應用程序實現的。

      環境2:

      ]# perf stat -e instructions,cycles  /root/code/DynamoRIO-AArch64-Linux-11.3.0                                                                                -1/bin64/drrun   -t drmemtrace -tool miss_analyzer -LL_miss_file rec.csv -- /root/code/dynamorio/clients/drcachesim/tests/stri                                                                                de_benchmark
      Value = 8960000
      ---- <application exited with code 0> ----
      Cache miss analyzer results:
      pc=0x4000052aec70, stride=384, locality=nta
      pc=0x4000052aefd8, stride=384, locality=nta
      pc=0x4000052ae934, stride=128, locality=nta
      pc=0x4009f0, stride=448, locality=nta
      
       Performance counter stats for '/root/code/DynamoRIO-AArch64-Linux-11.3.0-1/bin64/drrun -t drmemtrace -tool miss_analyzer -LL_                                                                                miss_file rec.csv -- /root/code/dynamorio/clients/drcachesim/tests/stride_benchmark':
      
           6,690,869,531      instructions              #    1.68  insn per cycle
           3,982,991,044      cycles
      
             1.294693889 seconds time elapsed
      
             1.482033000 seconds user
             0.048367000 seconds sys
      
      

      獲得的 IPC 值為 1.68

      • 周期計數(Cycle Accounting)

      作為確定工作負載性能瓶頸的第一步,查看周期計數相關事件所花費周期的分布情況。

      總cycle的83%在后端停頓,0%在前端停頓,這占用了由流水線停頓浪費的總體執行時間的83%。由于工作負載嚴重綁定后端,我們現在將逐一分析后端事件。我們已經從循環指針追逐代碼中得到了可能是內存綁定的提示,在這種情況下,指針追逐代碼本質上是對 CPU 緩存的訪問。
      接下來,我們將查看緩存性能和指令組合,因為它們可以揭示是否有什么因素導致我們的應用程序成為核心綁定,或者我們是否存在緩存性能問題。

      在環境2上結果如下:

      # perf stat -e cycles,stalled-cycles-frontend,stalled-cycles-backend,BR_RETIRED /root/code/DynamoRIO-AArch64-Linux-11.3.0-1/bin64/drrun   -t drmemtrace -tool miss_analyzer -LL_miss_file rec.csv -- /root/code/dynamorio/clients/drcachesim/tests/stride_benchmark
      Value = 8960000
      ---- <application exited with code 0> ----
      Cache miss analyzer results:
      pc=0x400011bb8c70, stride=384, locality=nta
      pc=0x400011bb8fd8, stride=384, locality=nta
      pc=0x400011bb8934, stride=128, locality=nta
      pc=0x4009f0, stride=448, locality=nta
      
       Performance counter stats for '/root/code/DynamoRIO-AArch64-Linux-11.3.0-1/bin64/drrun -t drmemtrace -tool miss_analyzer -LL_miss_file rec.csv -- /root/code/dynamorio/clients/drcachesim/tests/stride_benchmark':
      
           3,982,550,552      cycles
           1,469,978,223      stalled-cycles-frontend   #   36.91% frontend cycles idle
             168,951,085      stalled-cycles-backend    #    4.24% backend cycles idle
           1,526,351,269      BR_RETIRED
      
             1.295596696 seconds time elapsed
      
             1.477747000 seconds user
             0.052878000 seconds sys
      
      

      可見環境2中,主要是前端受限。

      • 數據緩存(D-Cache)效率

      由于 L2 是與 L1D-Cache 和 L1-ICache 統一的緩存,因此 L2 和最后一級緩存的未命中也可能是由前端的指令未命中引起的。在評估緩存性能時,檢查 L1-I 錯失次數總是有意義的。不過,對于這種工作負載,我們預計不會出現 L1-I miss,因為前端的滯留可以忽略不計。

      下表列出了一些詳細的緩存效率指標:

      L1D 緩存 MPKI 顯著高達 106,其中 53% 的 L1D 緩存訪問都是重新填充。二級緩存 MPKI 高達 78,53% 的訪問需要重新填充。此外,最后一級緩存讀取 MPKI 對內存帶寬資源造成了巨大壓力,因為 99% 的讀取請求沒有得到滿足,需要將LL 請求發回域內存。由于沒有 L1-I miss,二級緩存和末級緩存的壓力僅來自后端內存系統。
      接下來讓我們看看指令組合,了解內存指令的執行比例。

      # perf stat -e cycles,stalled-cycles-frontend,stalled-cycles-backend,BR_RETIRED,L1D_CACHE,L1D_CACHE_REFILL,L2D_CACHE,L2D_CACHE_REFILL,LL_CACHE_RD,LL_CACHE_MISS_RD /root/code/DynamoRIO-AArch64-Linux-11.3.0-1/bin64/drrun   -t drmemtrace -tool miss_analyzer -LL_miss_file rec.csv -- /root/code/dynamorio/clients/drcachesim/tests/stride_benchmark
      Value = 8960000
      ---- <application exited with code 0> ----
      Cache miss analyzer results:
      pc=0x40003a1fdc70, stride=384, locality=nta
      pc=0x40003a1fdfd8, stride=384, locality=nta
      pc=0x40003a1fd934, stride=128, locality=nta
      pc=0x4009f0, stride=448, locality=nta
      
       Performance counter stats for '/root/code/DynamoRIO-AArch64-Linux-11.3.0-1/bin64/drrun -t drmemtrace -tool miss_analyzer -LL_miss_file rec.csv -- /root/code/dynamorio/clients/drcachesim/tests/stride_benchmark':
      
           3,987,700,360      cycles
           1,466,330,193      stalled-cycles-frontend   #   36.77% frontend cycles idle
             176,866,560      stalled-cycles-backend    #    4.44% backend cycles idle
           1,527,527,743      BR_RETIRED
           2,701,320,530      L1D_CACHE
               6,651,603      L1D_CACHE_REFILL
              49,966,737      L2D_CACHE
              10,186,517      L2D_CACHE_REFILL
               7,861,886      LL_CACHE_RD
               1,358,060      LL_CACHE_MISS_RD
      
      • 指令混合

      指令組合顯示整數操作占 60%,加載指令占 20%,分支指令占 20%。由于我們的工作負載中存在明顯的設計缺陷,這說明該工作負載是內存受限的,需要進行優化以改善其緩存壓力,從而提高性能。
      雖然該工作負載不受限于前端,但仍值得檢查分支有效性計數,因為該工作負載也包含 20% 的分支。

      kperf收集指令混合數據

      # /home/qatest/script/kperf/kperf --imix --machine kunpeng920b /root/code/DynamoRIO-AArch64-Linux-11.3.0-1/bin64/drrun   -t drmemtrace -tool miss_analyzer -LL_miss_file rec.csv -- /root/code/dynamorio/clients/drcachesim/tests/stride_benchmark
      architecture kunpeng920b
      ============== Instruction Mix ===============
      ld_mix:                                 25.97%
      st_mix:                                 13.14%
      dp_mix:                                 37.83%
      ase_mix:                                 0.07%
      vfp_mix:                                 0.11%
      br_imm_mix:                             17.82%
      br_ret_mix:                              3.66%
      br_ind_mix:                              5.06%
      strex_fail_mix:                          0.00%
      crypto_mix:                              0.00%
      svc_mix:                                 0.00%
      
      • 分支效率

      為評估分支預測效果,下表列出了錯誤預測率和 MPKI 指標:

      該工作負載的分支預測錯誤率可忽略不計。否則,我們會看到前端停滯;這在意料之中。

      由于我們已經確定了工作負載的內存邊界,因此讓我們來看看 L1I TB 的性能如何。我們預計不會出現 L1I TLB 性能問題,因為我們的前端延遲可以忽略不計。

      • TLB效率

      TLB 效能 為了評估 TLB效率,我們統計了 MPKI 指標:

      指令側 TLB 錯失可以忽略不計,而數據側 TLB 的走表數顯著增加。這表明工作負載確實會產生數據側頁面未命中,從而導致某些內存訪問的頁表走行。

      kperf收集TLB效率

      # /home/qatest/script/kperf/kperf --tlb --machine kunpeng920b /root/code/DynamoRIO-AArch64-Linux-11.3.0-1/bin64/drrun   -t drmemtrace -tool miss_analyzer -LL_miss_file rec.csv -- /root/code/dynamorio/clients/drcachesim/tests/stride_benchmark
      architecture kunpeng920b
      =========== TLB Performance Metrics ===========
      ------------------ missrate -------------------
      l1i_tlb_missrate:                        0.24 %
      l1d_tlb_missrate:                        0.26 %
      l2i_tlb_missrate:                        0.16 %
      l2d_tlb_missrate:                        0.76 %
      ------------------ walk rate ------------------
      itlb_walk_rate:                          0.05 %
      dtlb_walk_rate:                          0.00 %
      -------------------- mpki ---------------------
      l1i_tlb_mpki:                              0.69
      l1d_tlb_mpki:                              1.09
      l2i_tlb_mpki:                              0.00
      l2d_tlb_mpki:                              0.01
      itlb_walk_mpki:                            0.13
      dtlb_walk_mpki:                            0.01
      
      
      • 工作負載特征總結

      下圖展示了所有MPKI和miss率的總結在一個圖表上。

      從特性數據來看,該工作負載嚴重受限于后端,后端停滯率高達 83%。該工作負載在數據緩存方面顯示出顯著的后端壓力,L1D MPKI 為 106,L2 MPKI 為 78,最后一級緩存讀取 MPKI 為 195。CPU 前端運行平穩,分支 MPKI、L1I MPKI 和 ITLB MPKI 等統計指標幾乎可以忽略不計,這與前端零停滯相對應。表征證據支持工作負載行為(如測試源代碼備注所述),即我們的系統存在內存瓶頸,下一步我們應研究如何解決這一問題。

      • 熱點分析事件

      為找出代碼執行瓶頸以優化代碼,我們應進一步深入研究兩個方面: 后端停滯和分層 D-Cache 事件。為進一步調查,除 INST_RETIRED 和 CPU_CYCLES 外,熱點分析的簡短事件列表包括
      L1D_CACHE、L1D_CACHE_REFILL、L2D_CACHE、L2D_CACHE_REFILL、LL_CACHE_RD、LL_CACHE_MISS_RD。

      參考資料

      5.1.2 第 2 階段:使用 Perf 事件采樣模式進行熱點分析

      我們收集了所有簡短列出的熱點分析事件的 perf 采樣數據,并研究了注釋反匯編代碼。

      • Stride 基準源代碼
      #include <stdint.h>
      #include <string.h>
      #include <iostream>
      
      #define MEM_BARRIER() __asm__ __volatile__("" ::: "memory")
      
      int
      main(int argc, const char *argv[])
      {
          // Cache line size in bytes.
          const int kLineSize = 64;
          // Number of cache lines skipped by the stream every iteration.
          const int kStride = 7;
          // Number of 1-byte elements in the array.
          const size_t kArraySize = 16 * 1024 * 1024;
          // Number of iterations in the main loop.
          const int kIterations = 20000;
          // The main vector/array used for emulating pointer chasing.
          unsigned char *buffer = new unsigned char[kArraySize];
          memset(buffer, kStride, kArraySize);
      
          // Add a memory barrier so the call doesn't get optimized away or
          // reordered with respect to callers.
          MEM_BARRIER();
      
          int position = 0;
      
          // Here the code will pointer chase through the array skipping forward
          // kStride cache lines at a time. Since kStride is an odd number, the main
          // loop will touch different cache lines as it wraps around.
          for (int loop = 0; loop < kIterations; ++loop) {
              // This prefetching instruction results in a speedup of >2x
              // on a Skylake machine running Linux when compiled with g++ -O3.
              // const int prefetch_distance = 5 * kStride * kLineSize;
              // __builtin_prefetch(&buffer[position + prefetch_distance], 0, 0);
      
              position += (buffer[position] * kLineSize);
              position &= (kArraySize - 1);
          }
      
          // Add a memory barrier so the call doesn't get optimized away or
          // reordered with respect to callers.
          MEM_BARRIER();
      
          std::cerr << "Value = " << position << std::endl;
      
          return 0;
      }
      
      • Stride 基準主函數反匯編
      # objdump -d /root/code/dynamorio/clients/drcachesim/tests/stride_benchmark
      /root/code/dynamorio/clients/drcachesim/tests/stride_benchmark:     file format elf64-littleaarch64
      
      
      Disassembly of section .init:
      
      0000000000400888 <_init>:
        400888:       d503201f        nop
        40088c:       a9bf7bfd        stp     x29, x30, [sp, #-16]!
      ...
      

      采樣周期和指令顯示,ldrb 指令的采樣率為 99%,該指令是通過追逐指針訪問數組元素的加載指令。指令和高速緩存缺失事件的熱點代碼區域也來自同一條指令,99% 的樣本也取自該指令,取樣于加載指令后的 “subs ”指令,該指令通過指針跟蹤訪問數組元素。反匯編代碼突出顯示了 “ldrb ”和 “subs ”指令。請注意,perf 采樣可以引入 kid,因此熱代碼行是加載后的 subs 指令,這是瓶頸所在。這表明該應用程序的主要瓶頸在于數組訪問的性能問題。

      5.1.3 第三階段:代碼優化

      眾所周知,減少內存壓力的一種優化方法是預取,可以通過硬件或軟件完成。在本例中,算法在每第七行緩存中追逐指針,通過仔細觀察,我們可以清楚地發現該跨度是有規律可循的。我們嘗試在該工作負載上進行軟件預加載,結果獲得了更好的性能,這表明硬件預取器在被測平臺上能有效地解決這一問題。在循環內添加了預處理器指令,該指令可實現軟件預加載,并有助于調整預取距離。

      對于 __builtin_prefetch 調整,我們使用了 (0,0) 選項來分別調整讀取訪問和預取到 L1 數據高速緩存,因為我們只加載一次數組元素,而且數據不會重復用于存儲在其他層次結構中。隨后,我們還針對多個預取距離值對軟件預加載器進行了訓練,直到在 DIST = 40 時獲得飽和的高性能,如圖所示。

      • 優化代碼特征總結

      通過軟件預加載,我們實現了 2 倍的性能提升--執行時間從 16 秒縮短至 8 秒。現在,讓我們看看如何在 PMU 事件中觀察優化代碼的性能提升,并與非優化代碼進行對比測量。

      觀察說明(表 7): 應用優化后,我們將周期減少了一半,性能提升了 2 倍。此外,IPC 也提高了約 3 倍,變化了 193%。需要注意的是,用于預取的額外代碼也使退役指令總數增加了 39.7%。讓我們比較一下優化代碼和基準代碼的反匯編,看看指令增加的情況?;€代碼和優化代碼之間的差異顯示了額外執行的指令,如下所示:讓我們看看指令組合是否反映了上表中的這些變化。

      不出所料,由于軟件預加載指令(prfm)被計入 LD_SPEC 事件,加載操作增加了一倍。其他指令則通過 DP_SPEC 事件計算,增加了 33%。

      由于我們的主要性能瓶頸是內存壓力,讓我們看看高速緩存有效性指標的改進。MPKI 并不是兩次運行之間的蘋果與蘋果比較,因為在軟件預加載的情況下,工作負載執行的指令要多出約 40%。因此,我們將只查看未命中率和訪問次數的變化,如表:

      觀察說明: L1 D-Cache 的訪問次數增加了一倍,因為優化后的代碼執行了兩倍的加載操作,預取指令也算作加載。由于我們專門在讀取模式下向 L1 進行了預取,因此我們能夠將 L1 D-Cache 的未命中率顯著降低 68%,從而使性能提升了 2 倍。L2 高速緩存訪問和 LL 高速緩存讀取訪問保持不變,因為我們沒有向這些層次中的任何層次進行預取。

      5.2 總結

      本案例研究演示了如何根據[第 2 章]中選定的 PMU 事件并按照[第 3 章]中的方法使用 Neoverse N1 內核的硬件 PMU 或進行工作負載鑒定。雖然該示例相當簡單,但它展示了一種方法,可用于隔離性能瓶頸的原因、糾正問題并驗證優化是否糾正了導致性能降級的行為。這個基本流程可以應用于更復雜的工作負載的分析性能問題。

      posted @ 2025-03-04 21:53  磁石空杯  閱讀(471)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产免费又黄又爽又色毛| 国产精品午夜福利资源| 国产精品一区二区性色av| 亚洲AV高清一区二区三区尤物 | 国产乱妇乱子视频在播放| 亚洲一区二区精品极品| 久久精品国产亚洲综合av| 国产色悠悠在线免费观看| 2021最新国产精品网站| caoporn成人免费公开| 丰满人妻被黑人猛烈进入| 无码日韩精品一区二区三区免费| 亚洲国产欧美一区二区好看电影| 四虎永久在线高清免费看| 天天躁日日摸久久久精品| 国产精品无遮挡猛进猛出| 中文字幕日韩有码一区| 成年女人永久免费观看视频| 久久涩综合一区二区三区| 久热这里有精彩视频免费| 无码人妻熟妇av又粗又大| 柠檬福利第一导航在线| 久久久久青草线综合超碰| 亚洲综合日韩av在线| 亚洲日韩中文字幕在线播放| 久久月本道色综合久久| 二区中文字幕在线观看| 日本美女性亚洲精品黄色| 国产精品亚洲综合久久小说| 久久婷婷大香萑太香蕉AV人| 动漫AV纯肉无码AV电影网| 亚洲熟妇少妇任你躁在线观看无码| 欧美人成精品网站播放| 色综合色综合久久综合频道| 欧美精品一产区二产区| 天天干天天干| 成人国产片视频在线观看| 116美女极品a级毛片| 久久久久无码中| 插入中文字幕在线一区二区三区| 国产99视频精品免费专区|