要:在實際應用中,影響并行加速比的因素主要是串行計算、并行計算和并行開銷三方面。
本文分享自華為云社區《高性能計算(2)——萬丈高樓平地起-云社區-華為云》,作者: 我是一顆大西瓜。
從物理劃分上 共享內存和分布式內存是兩種基本的并行計算機存儲方式 除此之外 分布式共享內存也是一種越來越重要的并行計算機存儲方式
根據指令的同時執行和數據的同時執行,計算機系統可以分成以下四類:
單處理器單數據就是“單CPU的機器”,它在單一的數據流上執行指令。在SISD中,指令被順序地執行。
對于每一個“CPU時鐘”,CPU按照下面的順序執行:
這種架構(馮·諾依曼體系)的主要元素有以下:
傳統的單處理器計算機都是經典的SISD系統。下圖表述了CPU在Fetch、Decode、Execute的步驟中分別用到了哪些單元:
這種模型中,有n個處理器,每一個都有自己的控制單元,共享同一個內存單元。在每一個CPU時鐘中,從內存獲得的數據會被所有的處理器同時處理,每一個處理器按照自己的控制單元發送的指令處理。在這種情況下,并行實際上是指令層面的并行,多個指令在相同的數據上操作。能夠合理利用這種架構的問題模型比較特殊,例如數據加密等。因此,MISD在現實中并沒有很多用武之地,更多的是作為一個抽象模型的存在。
SIMD計算機包括多個獨立的處理器,每一個都有自己的局部內存,可以用來存儲數據。所有的處理器都在單一指令流下工作;具體說,就是有n個數據流,每個處理器處理一個。所有的處理器同時處理每一步,在不同的數據上執行相同的指令。
很多問題都可以用SIMD計算機的架構來解決。這種架構另一個有趣的特性是,這種架構的算法非常好設計,分析和實現。限制是,只有可以被分解成很多個小問題(小問題之間要獨立,可以不分先后順序被相同的指令執行)的問題才可以用這種架構解決。很多超級計算機就是使用這架構設計出來的。例如Connection Machine(1985年的 Thinking Machine)和MPP(NASA-1983).我們在第六章 GPU Python編程中會接觸到高級的現代圖形處理器(GPU),這種處理器就是內置了很多個SIMD處理單元,使這種架構在今天應用非常廣泛。
在費林分類中,這種計算機是最廣泛使用、也是最強大的一個種類。這種架構有n個處理器,n個指令流,n個數據流。每一個處理器都有自己的控制單元和局部內存,讓MIMD架構比SIMD架構的計算能力更強。每一個處理器都在獨立的控制單元分配的指令流下工作;因此,處理器可以在不同的數據上運行不同的程序,這樣可以解決完全不同的子問題甚至是單一的大問題。在MIMD中,架構是通過線程或進程層面的并行來實現的,這也意味著處理器一般是異步工作的。這種類型的計算機通常用來解決那些沒有統一結構、無法用SIMD來解決的問題。如今,很多計算機都應用了這中間架構,例如超級計算機,計算機網絡等。然而,有一個問題不得不考慮:異步的算法非常難設計、分析和實現。
老生常談,線程和進程的區別和聯系:
計算機系統是由一個或多個物理處理器和內存組成,運行的程序會將內存分為兩個部分,一部分是共享變量使用的存儲區域, 另一部分供各線程的私有變量使用的存儲區域。線程綁定是將線程綁定在固定的處理器上, 從而在線程與處理器之間建立一對一的映射關系。如果不進行線程綁定,線程可能在不同的時間片運行在不同的處理器上。我們知道,每個處理器是有自己的多級緩存的,如果線程切來切去,那么cache命中率肯定不高,程序性能也會受到影響。通過線程綁定,程序能夠獲得更高的cache利用率從而提高程序性能。c++中如何進行線程綁定可以參考https://www.cnblogs.com/wenqiang/p/6049978.html
理論上來說,n個相同的cpu理論上能提供n倍的計算能力。
但是在實際過程中,并行開銷會導致總的執行時間無法線性地減少。這些開銷分別為:
加速比的定義是順序程序執行時間除以計算同一結果的并行程序的執行時間
式中,t_sts為一顆CPU程序完成該任務所需串行執行時間;t_ptp為n顆CPU并行執行完成該任務所需時間。由于串行執行時間t_sts?為n顆CPU并行執行完成該 和并行執行時間t_ptp?有多種定義方式。 這樣就產生了五種不同的加速比的定義,即相對加速比、實際加速比、絕對加速比、漸近實際加速比和漸近相對加速比。
在實際應用中,影響并行加速比的因素主要是串行計算、并行計算和并行開銷三方面。一般情況下, 并行加速比小于CPU的數量。但是,有時會出現一種奇怪的現象,即并行程序能以串行程序快n倍的速度運行,稱為超線性加速比。產生超線性加速的原因在于CPU訪問的數據都駐留在各自的高速緩存Cache中, 而高速緩存的容量比內存要小, 但讀寫速度卻遠高于內存。
衡量并行算法的另一個主要標準是并行效率,它表示的是多顆CPU在進行并行計算時單顆CPU的平均加速比。
理想并行效率為1表明全部CPU都在滿負荷工作。通常情況下,并行效率會小于1, 且隨CPU數量的增加而減小。
伸縮性用于度量并行機器高效運行的能力,代表跟處理器數量成比例的計算能力 (執行速度)。如果問題的規模和處理器的數量同時增加,性能不會下降。
阿姆德爾定律廣泛使用于處理器設計和并行算法設計。它指出程序能達到的最大加速比被程序的串行部分限制。$S=1/(1-p) $中 1-p1?p 指程序的串行部分。它的意思是,例如一個程序90%的代碼都是并行的,但仍存在10%的串行代碼,那么系統中即使由無限個處理器能達到的最大加速比仍為9。
古斯塔夫森定律在考慮下面的情況之后得出的:
古斯塔夫森定律指出了加速比S(P)=P-\alpha (P-1)S(P)=P?α(P?1), PP 為處理器的數量, SS S 為加速比,\alphaα 是并行處理器中的非并行的部分。作為對比,阿姆德爾定律將單個處理器的執行時間作為定量跟并行執行時間相比。因此阿姆德爾定律是基于固定的問題規模提出的,它假設程序的整體工作量不會隨著機器規模 (也就是處理器數量) 而改變。古斯塔夫森定律補充了阿姆德爾定律沒有考慮解決問題所需的資源總量的不足。古斯塔夫森定律解決了這個問題, 它表明設定并行解決方案所允許耗費的時間的最佳方式是考慮所有的計算資源和基于這類信息。
點擊下方,第一時間了解華為云新鮮技術~
華為云博客_大數據博客_AI博客_云計算博客_開發者中心-華為云
Hamsters是一個能讓JavaScript代碼并行執行的原生庫,它可以讓你面向高性能的JavaScript編程,它是一個開源項目,Github上標星4k+。它的目的就是讓你更加容易的利用多線程的強大功能來并行編程實現性能的提升!
https://github.com/austinksmith/Hamsters.js
安裝使用
bower install WebHamsters //OR npm install hamsters.js
<!-- HTML4 and (x)HTML --> <script type="text/javascript" src="path/to/hamsters.js"> <!-- HTML5 --> <script src="path/to/hamsters.js"></script>
import hamsters from 'path/to/hamsters';
import Worker from '...'; import hamsters from 'hamsters.js'; hamsters.init({ Worker: Worker });
var hamsters=require('hamsters.js');
var Worker=require('...').Worker; var hamsters=require('hamsters.js'); hamsters.init({ Worker: Worker });
第一個要理解的是Hamsters.js是一個傳遞interfafce的消息,因此在使用庫調用函數時,我們需要通過將params對象(消息)傳遞給庫來指示庫如何操作。
var params={ bar: 'foo' }; hamsters.run(params, ....);
我們將使用的下一個參數將是我們想要在一個線程或線程中執行的邏輯,我們之前傳遞的params對象將在我們的函數的上下文中可訪問。現在應該能夠看到如何確保可以在線程中訪問變量和函數等不同的東西。
hamsters.run(params, function() { var foo=params.bar; });
第三個也是最后一個參數將是我們的onSuccess回調方法,此函數所需的唯一參數是輸出。
hamsters.run(params, function() { var foo=params.bar; }, function(results) { console.log(results); });
回到原始的params對象,為了從庫中獲得最佳性能和可靠性,需遵循一些約定。Hamsters.js的構建目標是并行而不是并發,盡管庫很好地實現了并行執行的主要目標。由于這樣做的各種設計決策是為了幫助實現這一目標,其中一個決定是庫如何在線程之間分割數據以便執行,因此您希望在多個線程中訪問的任何數組必須在您的參數內具有數組索引賓語。
var params={ array: [1, 2, 3, 4]; }; hamsters.run(params, function() { for(var i=0; i < params.array; i++) { rtn.data.push(params.array[i] * 4); } }, function(results) { });
使用此約定,通過簡單地更改params對象中的一個選項,可以非常簡單地并行化上述方法。現在使用下面的方法,4個線程將完成相同的任務,每個線程僅在數組的一個數字上運行。
var params={ array: [1, 2, 3, 4]; threads: 4 }; hamsters.run(params, function() { for(var i=0; i < params.array; i++) { rtn.data.push(params.array[i] * 4); } }, function(results) { });
更進一步,庫使用一個名為rtn的內部返回對象,這個rtn對象對于庫具有一致的方式來處理線程輸出是至關重要的。因此,當我們想要從線程返回一個值時,我們需要將結果推送到rtn.data數組中。或者你可以讓你的rtn.data輸出,但只有當你的輸出已經是一個數組。
hamsters.run(params, function() { rtn.data.push(params.bar); }, function(results) { console.log(results); // 'foo'; });
通過以上代碼來看下它傳遞的參數
var params={ threads: Integer, aggregate: Boolean, dataType: String, memoize: Boolean sort: String, };
1、threads這個可選參數將告訴庫執行先前聲明的函數的線程數,這允許在非常簡單的級別上更改您執行的線程數。如果您在此處未提供值,則庫默認值為1。
2、aggregate此可選參數將告訴庫我們是否要在執行后將各個線程輸出聚合在一起,這僅在您跨多個線程執行并且默認為相關時才相關false。
3、dataType此可選參數將通知庫我們的數據數組是JavaScript的類型化數組之一,在使用此參數時,庫將自動格式化輸出以匹配指定的輸出dataType。
4、memoize此可選參數旨在與memoization模式結合使用,當啟用memoization模式時,此參數允許用戶控制單個函數級別是否緩存該函數的結果,其默認值為false。
5、sort此可選參數將告訴庫按字母順序或數字順序自動對最終輸出進行排序,此參數的默認值為,null并且可以使用排序選項進行配置。
params對象中包含的任何其他內容都可以在線程的執行上下文或多個線程中訪問,具體取決于您使用庫的方式。
除了以上基本使用方式,你可以查看官方的相關文檔,有詳細的介紹和使用方式,如Promise、排序、memoization、可轉移對象、persistence、線程池、限制、設備等
多線程和并行編程在Javascript中本身不是一件容易的事情,但是可借助第三方原生庫來彌補它,能讓你更加方便的進行多線程的編程,筆者可能并未介紹的非常清楚,如果你有這方面的需求,可以去查案Hamsters.js的相關文檔來體驗這種編程,希望對你有所幫助!
要:在本文中,將重點關注網頁的初始渲染,即它從解析 HTML 開始。 我將探索可能導致高渲染時間的問題,以及如何解決它們。
本文分享自華為云社區《頁面首屏渲染性能指南-云社區-華為云》,作者:Ocean2022。
我們知道渲染頁面是一個將服務器的響應內容翻譯成圖片的過程。但是,如果你頁面的渲染性能比較糟糕的話,可能會帶來相對較高的跳出率。
在本文中,我將重點關注網頁的初始渲染,即它從解析 HTML 開始。 我將探索可能導致高渲染時間的問題,以及如何解決它們。
關鍵渲染路徑 (CRP) 是瀏覽器將代碼轉換為屏幕上可顯示像素的過程。 它有幾個階段,其中一些可以并行執行以節省時間,但有些部分必須依次完成。 如下圖所示:
首先,一旦瀏覽器得到響應,它就會開始解析它。 當它遇到依賴項時,它會嘗試下載它。 如果它是一個樣式表文件,瀏覽器必須在渲染頁面之前完全解析它,這就是為什么 CSS 會阻塞渲染的原因。
如果是腳本,瀏覽器必須:停止解析,下載腳本,然后運行。 只有在那之后它才能繼續解析,因為 JavaScript 程序可以改變網頁的內容(尤其是 HTML)。 這就是為什么 JS 會阻塞解析的原因。
完成所有解析后,瀏覽器將構建文檔對象模型 (DOM) 和級聯樣式表對象模型 (CSSOM)。 將它們組合在一起得到渲染樹。 頁面的不顯示部分不會進入渲染樹,因為它只包含繪制頁面所需的數據。
倒數第二步是將渲染樹進行布局, 這個階段也稱為回流:就是計算每個渲染樹節點的每個位置及其大小的地方。
最后一步是繪制。 它會根據瀏覽器在前一階段計算得到的數據對像素進行著色。
因此,根據這一過程,我們在優化性能方面,得出了一些結論。如果你要提升頁面初始化渲染的性能,你需要:
同時,我們會根據下面 3 個指標來衡量優化的效率:
除了渲染時間之外,還有其他一些因素也需要考慮。例如,你的頁面使用了多少阻塞資源以及下載它們需要多長時間。
鑒于我們在上面得出的結論,我們得出網站性能優化有三種主要策略:
首先,移除所有未使用的部分,例如 JavaScript 中無法訪問的函數、帶有從不匹配任何元素的選擇器的樣式以及被 CSS 永遠隱藏的 HTML 標簽。 其次,刪除所有重復項。
然后,我建議建立一個自動壓縮過程。 例如,它應該從你的后端服務中刪除所有注釋(但不是源代碼)以及每個不包含附加信息的字符(例如 JS 中的空白字符)。
完成后,我們剩下的可以是文本字符串。 這意味著我們可以安全地應用諸如 GZIP(大多數瀏覽器都理解)之類的壓縮算法。
最后,還有緩存。 瀏覽器第一次呈現頁面時它不會有幫助,但它會在以后的訪問中節省很多。 但是,記住兩點至關重要:
當然,應該為每個資源定義緩存策略。 有些可能很少改變或根本不會改變,有的則是變化的很快,還有些文件包含敏感的信息(可以使用 “private” 防止 CDN 緩存私有數據)。
“關鍵”僅指網頁正確呈現所需的資源。 因此,我們可以直接跳過所有流程中沒有涉及的樣式以及腳本文件。
為了告訴瀏覽器不需要特定的 CSS 文件,我們應該為所有引用樣式表的鏈接設置媒體屬性。 使用這種方法,瀏覽器將只根據需要處理與當前媒體(設備類型、屏幕尺寸)匹配的資源,同時降低所有其他樣式表的優先級。 例如,如果你將 media=“print” 屬性添加到引用樣式以打印頁面的樣式標記,則這些樣式不會在不打印媒體時干擾你的關鍵渲染路徑。
為了進一步改進該過程,你還可以將一些樣式內聯,這可以為我們節省了至少一次到服務器的往返行程。
如上所述,腳本會阻塞解析,因為它們可以改變 DOM 和 CSSOM。 為了避免這一點,所有腳本標簽都必須用屬性標記——異步或延遲。
標有 async 的腳本不會阻塞 DOM 構建或 CSSOM,因為它們可以在 CSSOM 構建之前執行。 但請記住,內聯腳本無論如何都會阻止 CSSOM,除非你將它們放在 CSS 之上。
相比之下,標有 defer 的腳本將在頁面加載結束時進行執行。
換句話說,使用 defer,腳本直到頁面加載事件被觸發后才會執行,而 async 讓腳本在文檔被解析時就會在后臺運行。
最后,應將 CRP 長度縮短到可能的最小值。
作為樣式標簽屬性的媒體查詢將減少必須下載的資源總數。 script 標簽屬性 defer 和 async 將防止相應的腳本阻塞解析。
使用 GZIP 壓縮、壓縮和歸檔資源將減少傳輸數據的大小(從而也減少數據傳輸時間)。
內聯一些樣式和腳本也可以減少瀏覽器和服務器之間的往返次數。
按照最新的最佳性能實踐理念,一個網站應該做的最快的第一件事就是展示 ATF 內容。 ATF 代表首屏。 這是立即可見的區域,無需滾動。 因此,最好以首先加載所需樣式和腳本的方式重新排列與渲染相關的所有內容,而其他所有內容都停止(既不解析也不渲染)。
總而言之,網站性能優化包含了網站響應的各個方面,例如緩存、設置 CDN、重構、資源優化等,但是所有這些都可以逐步完成。 作為 Web 開發人員,你可以將本文作為參考,并始終記住在實驗之前和之后測量性能。
瀏覽器開發人員盡最大努力優化你訪問的每個頁面的網站性能,這就是瀏覽器通常實現所謂的“預加載器”的原因。 這部分程序會在你以 HTML 格式請求的資源之前進行掃描,以便一次發出多個請求并讓它們并行運行。 這就是為什么在 HTML(逐行)以及腳本標簽中保持樣式標簽彼此靠近的原因。
此外,嘗試批量更新 HTML 以避免多個布局事件,這些事件不僅由 DOM 或 CSSOM 中的更改觸發,而且在設備方向更改和窗口大小調整時也會觸發。
點擊下方,第一時間了解華為云新鮮技術~
華為云博客_大數據博客_AI博客_云計算博客_開發者中心-華為云
*請認真填寫需求信息,我們會在24小時內與您取得聯系。