(done) 并行計算 CS149 Lecture2 (現代多核處理器) (SIMD, 多核, 超標量, 數據預取, 超線程)

視頻 url: https://www.bilibili.com/video/BV1du17YfE5G?spm_id_from=333.788.videopod.sections&vd_source=7a1a0bc74158c6993c7355c5490fc600&p=2

大佬筆記 url: https://zhuanlan.zhihu.com/p/8129089606


先看視頻:

Lecture 0 ~ 28min 的內容基本就是 cache,本科體系結構學過的東西。

sinx例子:串行版本

一個例子貫穿全課(使用泰勒展開計算 sinx):
該程序使用泰勒展開實現 sinx() 函數。為每一個 x (x數組中的每一個元素) 計算一個 y (y 數組中的每一個元素)。這是一個串行程序

void sinx(int N, int terms, float* x, float* y)
{for (int i = 0; i < N; i++){float value = x[i];float numer = x[i] * x[i] * x[i];int denom = 6; // 3!int sign = -1;for (int j = 1; j < terms; j++){value += sign * numer / denom;numer *= x[i] * x[i];denom *= (2 * j + 2) * (2 * j + 3);sign *= -1;}y[i] = value;}
}

sinx例子:C++ thread 雙線程版本

上面這個程序可以通過改變程序的方式來實現并行,比如,我們可以通過 C++ thread,增加一個線程幫助我們實現并行:(下面這個代碼應該是有 bug 的,但是無傷大雅)
但下面的代碼有個問題:如果我的硬件有4個 CPU cores,那么下面的代碼只能利用到我的兩個 CPU cores,而非四個。

#include <thread>void sinx(int N, int terms, float* x, float* y)
{for (int i = 0; i < N; i++){float value = x[i];float numer = x[i] * x[i] * x[i];int denom = 6; // 3!int sign = -1;for (int j = 1; j < terms; j++){value += sign * numer / denom;numer *= x[i] * x[i];denom *= (2 * j + 2) * (2 * j + 3);sign *= -1;}y[i] = value;}
}typedef struct {int N;int terms;float* x;float* y;
} my_args;void my_thread_func(my_args* args)
{sinx(args->N, args->terms, args->x, args->y); // do work
}void parallel_sinx(int N, int terms, float* x, float* y)
{std::thread my_thread;my_args args;args.N = N/2;args.terms = terms;args.x = x;args.y = y;my_thread = std::thread(my_thread_func, &args); // launch threadsinx(N - args.N, terms, x + args.N, y + args.N); // do work on main threadmy_thread.join(); // wait for thread to complete
}

sinx例子:高級語言循環并行化版本

事實上,現代高級編程語言幾乎都有這么一種抽象語義,來表示一個循環的每次迭代都是相互獨立的,比如 C+OpenMP, pyTorch 等等,如下:
高級編程語言會根據硬件 cores 的數量,自動創建合適數量的線程去并行執行下面的代碼。也就是說,如果我有 16 個 CPU cores,那么通常高級編程語言會幫助我把計算任務平均地分配給 16 個 CPU cores。
在這里插入圖片描述

sinx例子:SIMD 版本

這個課堂給出的 SIMD 例子需要硬件和編譯器的支持。

硬件上,要求一個 core 里有多個 ALU 元件,如下:
在這里插入圖片描述

使用一個 ALU 的程序叫做 scalar program (標量程序)。

如下,使用 AVX 指令的代碼則叫做 vector program (矢量程序)

#include <immintrin.h>void sinx(int N, int terms, float* x, float* y)
{float three_fact = 6; // 3!for (int i = 0; i < N; i += 8){__m256 origx = _mm256_load_ps(&x[i]);__m256 value = origx;__m256 numer = _mm256_mul_ps(origx, _mm256_mul_ps(origx, origx));__m256 denom = _mm256_broadcast_ss(&three_fact);int sign = -1;for (int j = 1; j < terms; j++){// value += sign * numer / denom__m256 tmp = _mm256_div_ps(_mm256_mul_ps(_mm256_set1_ps(sign), numer), denom);value = _mm256_add_ps(value, tmp);numer = _mm256_mul_ps(numer, _mm256_mul_ps(origx, origx));denom = _mm256_mul_ps(denom, _mm256_set1_ps((2*j+2) * (2*j+3)));sign *= -1;}_mm256_store_ps(&y[i], value);}
}

上面的 SIMD 源碼會被編譯器編譯成下面的 SIMD 匯編指令,一個指令處理 256-bit 數據,從而加速
在這里插入圖片描述

一個問題是,為什么選擇在單核中增加 ALU,而不是直接增加核數?
回答:一個 ALU 相比一個完整的 CPU core 便宜很多

SIMD遇到分支判斷結構怎么辦?(使用掩碼濾除)

如下圖,當 SIMD 代碼遇到分支判斷結構時,可能無法同時執行 SIMD 指令。因為每次循環執行的指令流并不一致。(這種情況也叫做 “線程分化”)
此時有一種很直接的做法:讓 CPU core 使用 SIMD 指令同時執行 if-else 中的所有代碼,隨后根據 if 判斷的結果掩蓋掉部分計算結果。
在這里插入圖片描述

一個具體的例子如下:
源碼:

// 偽代碼:每個線程處理一個元素
if (data[i] > 5) {data[i] *= 2;  // 分支導致線程分化
}

生成掩碼:

mask = (data > 5)  # 例如:mask = [1, 0, 1, ...]

應用掩碼:

// 偽代碼:所有線程執行相同指令,但僅掩碼為1的通道生效
data = data * (2 * mask + (1 - mask))  // 滿足條件時乘2,否則乘1(即不變)

更具體的例子:
Intel AVX512 指令集的掩碼操作

// 示例:Intel AVX512 指令集的掩碼操作
__mmask16 mask = _mm512_cmp_ps_mask(vec, threshold, _CMP_GT_OS);
result = _mm512_mask_mul_ps(vec, mask, vec, factor); // 僅掩碼為1的通道執行乘法

CUDA 中使用掩碼選擇活躍線程

// 示例:CUDA 中使用掩碼選擇活躍線程
unsigned int mask = __ballot_sync(0xFFFFFFFF, data[i] > 5);
if (threadIdx.x % 32 < __popc(mask)) {// 僅滿足條件的線程執行后續操作
}

在課堂例子中,極端情況下只有 1/8 的效率。
比如,只有線程1執行 if-True 的情況,其它7個線程執行 if-False的情況。
而 if-True 包含 expensive 的代碼,if-False 包含 cheap 的代碼。
那么,哪怕使用了上述掩碼技術,效率仍然接近 1/8
在這里插入圖片描述

指令流一致性和發散執行

指令流一致性:多個計算單元執行的指令序列是一致的
對于SIMD并行來說,指令流一致性是必要的
但對于多核并行來說,指令流一致性不必要,因為每個 core 有自己的 IFU 和 IDU。
發散執行:指的是一個程序中缺少 “指令流一致性”
在這里插入圖片描述

SIMD 需要 CPU硬件支持、編譯器支持、以及程序員的參與
在這里插入圖片描述

三種不同的并行形式

1.超標量:例如 nutshell 的順序雙發、以及BOOM的亂序多發處理器,這種并行由CPU自己執行,沒有編譯器和程序員的參與。
2.SIMD:利用一個 CPU core 上多個 ALU,需要硬件、編譯器和程序員的支持。
3.多核:在多個 CPU core 上運行同一個程序的多個線程。需要硬件、編譯器和程序員的支持。
在這里插入圖片描述

下圖很好地介紹了三種并行的區別
在這里插入圖片描述

這些并行形式可以混合,如下:
在這里插入圖片描述在這里插入圖片描述

數據預取(緩解內存IO開銷)

內存訪問仍然是一個大的性能瓶頸,除了緩存外,還有數據預取,如下
在這里插入圖片描述

超線程技術(緩解IO開銷)

還有一種解決內存瓶頸的方式:在 CPU core 里實現多個 execution context。(也叫超線程技術)
當遇到內存讀取 cache miss 時,CPU 知道自己要等待很久,于是切換到第二個 thread 的 execution context 去執行第二個 thread
在這里插入圖片描述

需要注意的是,上述方法提高了 overall system throughput,但實際上降低了單個線程的執行速度。(因為單個線程在內存數據讀取完畢后,CPU core 并沒有立刻切換回來執行)
在這里插入圖片描述


再來看大佬筆記補充

3 實現SIMD的AVX2案例代碼

SIMD這一思想對應的指令集就是我們常常在硬件評測類視頻或文章中提到的AVXxxx系列指令集。AVX2是目前使用最廣泛的SIMD指令集。AVX512支持的SIMD指令集的寬度更大, 但是目前還沒有普及, 而且因為發熱過大還在Intel最近幾代CPU被移除了, 這里的案例代碼也是基于AVX2的, 其支持的SIMD指令寬度為256位。

查看個人PC是否支持AVX2指令集的方法:

cat /proc/cpuinfo | grep avx2

3.1 中常用的函數總結

3.1.1 浮點運算函數

在這里插入圖片描述
注意事項: 1. 函數名中的 ps 表示 packed single (單精度浮點) 2. 函數名中的 pd 表示 packed double (雙精度浮點) 3. 256 表示使用 256 位寄存器 4. 單精度運算一次處理 8 個數 (256/32=8) 5. 雙精度運算一次處理 4 個數 (256/64=4)


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/74796.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/74796.shtml
英文地址,請注明出處:http://en.pswp.cn/web/74796.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Leetcode 3508. Implement Router

Leetcode 3508. Implement Router 1. 解題思路2. 代碼實現 題目鏈接&#xff1a;3508. Implement Router 1. 解題思路 這一題就是按照題意寫作一下對應的函數即可。 我們需要注意的是&#xff0c;這里&#xff0c;定義的類當中需要包含以下一些內容&#xff1a; 一個所有i…

Linux: 系統內核中的信號

目錄 一 前言 二 信號在內核中的表示 三 sigset_t 四 信號集操作 1. sigpending() 2. sigemptyset() 3. sigfillset() 4. sigaddset ()和sigdelset() 5. sigismember() 6. sigprocmask() 五 深入理解信號的捕捉流程 一 前言 在Linux: 進程信號初識-CSDN博客信…

Nginx-keepalived-高可用

Nginx 高可用 通常 借助 Keepalived 實現&#xff0c; Keepalived 能通過 VRRP &#xff08;虛擬路由冗余協議&#xff09;讓多個 Nginx 服務器 組成一個 熱備集群&#xff0c;當主服務器故障時自動切換到備用服務器&#xff0c;保障服務不間斷。 一、環境準備 角色IP 地址主…

使用python完成手寫數字識別

入門圖像識別的第一個案例,看到好多小伙伴分享,也把自己當初的思路捋捋,寫成一篇博客,作為記錄和分享,也歡迎各位交流討論。 實現思路 數據集:MNIST(包含60,000個訓練樣本和10,000個測試樣本) 深度學習框架:Keras(基于TensorFlow) 模型架構:卷積神經網絡(CNN) 實…

Java學習總結-多線程-三種創建方法

什么是線程&#xff1f; 線程&#xff08;Thread&#xff09;是程序內部的一條執行流程。 程序如果只有一條執行流程&#xff0c;那這個程序就是單線程程序。 什么是多線程&#xff1f; 多線程是指從軟硬件上實現的多條執行流程的技術&#xff08;多條線程由CPU負責調度執行…

電動垂直起降飛行器(eVTOL)

電動垂直起降飛行器&#xff08;eVTOL&#xff09;的詳細介紹&#xff0c;涵蓋定義、技術路徑、應用場景、市場前景及政策支持等核心內容&#xff1a; 一、定義與核心特性 eVTOL&#xff08;Electric Vertical Take-off and Landing&#xff09;即電動垂直起降飛行器&#xf…

ensp 網絡模擬器 思科華為基于VLANIF的公司網絡搭建

該文章僅記錄作業配置過程 如有雷同純屬巧合 一. 其它&#xff08;共1題&#xff0c;100分&#xff09; 1. (其它) 為大學生公司創建部門VLAN 1.項目 背景 為大學生公司現有財務部、技術部和業務部&#xff0c;出于數據安全的考慮&#xff0c;各部門的計算機需進行隔離。公…

使用`sklearn`中的邏輯回歸模型進行股票的情感分析,以及按日期統計積極和消極評論數量的功能

以下是完成上述任務的Python代碼&#xff0c;可在Jupyter Notebook中運行。此代碼包含了使用sklearn中的邏輯回歸模型進行情感分析&#xff0c;以及按日期統計積極和消極評論數量的功能。 import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer f…

oracle批量刪除分區

為了清理數據&#xff0c;往往需要刪除一些分區 簡單查看當前分區 附件 --創建測試表 -- drop table test_part purge;CREATE TABLE test_part (sales_id NUMBER,sale_date DATE,amount NUMBER ) PARTITION BY RANGE (sale_date) INTERVAL (INTERVAL 1 MONTH) -- 每個月創建…

java流程控制08:For循環

For循環 雖然所有循環結構都可以用while或者do…while表示&#xff0c;但Java提供了另一種語句-----for循環&#xff0c;使一些循環結構變得更加簡單。 for循環語句是支持迭代的一種通用結構&#xff0c;是最有效、最靈活的循環結構。 for循環執行的次數是在執行前就確定的。…

嵌入式軟件開發調試方法

文章目錄 1. 利于函數返回值,retrurn 定位錯誤位置2. 合理使用邏輯分析儀&#xff08;正點原子 厲害&#xff01;&#xff01;&#xff09; 1. 利于函數返回值,retrurn 定位錯誤位置 如下圖所示&#xff0c;設置不同的返回值&#xff0c;0是ok的&#xff0c;其他值均為失敗&…

P1025 [NOIP 2001 提高組] 數的劃分(DFS)

題目描述 將整數 n 分成 k 份&#xff0c;且每份不能為空&#xff0c;任意兩個方案不相同&#xff08;不考慮順序&#xff09;。 例如&#xff1a;n7&#xff0c;k3&#xff0c;下面三種分法被認為是相同的。 1,1,5; 1,5,1; 5,1,1. 問有多少種不同的分法。 輸入格式 n,k …

設計模式簡述(三)工廠模式

工廠模式 描述簡單工廠&#xff08;靜態工廠&#xff09;工廠方法模式 抽象工廠增加工廠管理類使用 描述 工廠模式用以封裝復雜的實例初始化過程&#xff0c;供外部統一調用 簡單工廠&#xff08;靜態工廠&#xff09; 如果對象創建邏輯簡單且一致&#xff0c;可以使用簡單工…

批量將 JSON 轉換為 Excel/思維導入等其它格式

json 格式相信對大家來說都不陌生&#xff0c;這是一種輕量級的結構化數據&#xff0c;可以對對象進行描述。json 格式也是一種普通的文本文件格式&#xff0c;用記事本就能夠打開編輯 json 格式的文件&#xff0c;可以很方便的轉換為其他格式。今天要給大家介紹的就是如何將 j…

電腦有時出現檢測不到音箱設備怎么辦?

問題 有時候電腦開機之后就檢測不到音箱&#xff0c;經過我一頓檢查發現是檢測不到聲卡&#xff0c;即使拔插了音箱也沒用&#xff0c;但是當我重啟或者休眠之后再重啟發現就檢測到了 解決方案 方案一 重啟或者休眠之后再開啟 方案二 使用powershell指令將聲卡彈出和載入…

Qwen-Agent框架的文件相關操作:從Assistant到BasicDocQA

在前面的幾篇文章如《針對Qwen-Agent框架的Function Call及ReAct的源碼閱讀與解析&#xff1a;Agent基類篇》 、《基于Qwen-Agent框架的Function Call及ReAct方式調用自定義工具》、 《針對Qwen-Agent框架的源碼閱讀與解析&#xff1a;FnCallAgent與ReActChat篇》中&#xff0c…

RSSI定位程序,N個錨點、三維空間,使用CKF對軌跡進行濾波,附MATLAB代碼的下載鏈接

本文所述的程序實現三維空間中基于RSSI信號的多錨點定位&#xff0c;并采用容積卡爾曼濾波&#xff08;CKF&#xff09;對動態軌跡進行降噪優化。代碼包含完整的定位仿真流程&#xff0c;涵蓋環境建模、信號強度模擬、定位解算、軌跡濾波及可視化分析模塊 文章目錄 程序介紹概述…

開源軟件與自由軟件:一場理念與實踐的交鋒

在科技的世界里&#xff0c;“開源軟件”和“自由軟件”這兩個詞幾乎無人不知。很多人或許都聽說過&#xff0c;它們的代碼是公開的&#xff0c;可以供所有人查看、修改和使用。然而&#xff0c;若要細究它們之間的區別&#xff0c;恐怕不少朋友會覺得云里霧里。今天&#xff0…

C++ - 頭文件基礎(常用標準庫頭文件、自定義頭文件、頭文件引入方式、防止頭文件重復包含機制)

一、頭文件 在 C 中&#xff0c;頭文件&#xff08;.h&#xff09;用于函數聲明、類定義、宏定義等等 在 Visual Studio 中&#xff0c;頭文件通常放在頭文件目錄中&#xff0c;頭文件實現通常放在源文件目錄中 二、常用標準庫頭文件 1、輸入輸出 <iostream> 標準輸入…

CSS 背景屬性學習筆記

一、CSS 背景屬性概述 CSS 背景屬性用于定義 HTML 元素的背景效果&#xff0c;主要包括以下幾種屬性&#xff1a; background-color&#xff1a;定義元素的背景顏色。 background-image&#xff1a;定義元素的背景圖像。 background-repeat&#xff1a;定義背景圖像如何重復…