【CUDA】 Trust基本特性介紹及性能分析

Trust簡介

Thrust 是一個實現了眾多基本并行算法的 C++ 模板庫,類似于 C++ 的標準模板庫(standard template library, STL)。該庫自動包含在 CUDA 工具箱中。這是一個模板庫,僅僅由一些頭文件組成。在使用該庫的某個功能時,包含需要的頭文件即可。該庫中的所有類型與函數都在命名空間thrust中定義,所以都以thrust::開頭。用命名空間的目的是避免名稱沖突。例如,Thrust中的thrust::sort和STL 中的 std::sort 就不會發生名稱沖突。

數據結構

Thrust 中的數據結構主要是矢量容器(vector container),類似于 STL中的std::vector。在 Thrust 中,有兩種矢量:

(1)一種是存儲于主機的矢量 thrust::host_vector<typename>。

(2)一種是存儲于設備的矢量 thrust::device_vector<typename>。這里的 typename 可以是任何數據類型。例如,下面的語句定義了一個設備矢量x,元素類型為雙精度浮點數(全部初始化為0),長度為10:

thrust::device_vector<double>x(10,0);

要使用這兩種矢量,需要分別包含如下頭文件:

#incldue <thrust/host vector.h>

#incldue <thrust/device vector.h>

算法

Thrust 提供了5類常用算法,包括

(1)變換(transformation)。

(2)歸約(reduction)。

(3)前綴和(prefxsum)。

(4)排序(sorting)與搜索(searching)。

(5)選擇性復制、替換、移除、分區等重排(reordering)操作。

除了 thrust::copy,Thrust 算法的參數必須都來自于主機矢量或都來自于設備矢量。否則,編譯器會報錯。


實例分析

在了解 Thrust 庫更多的細節之前,我們先分析Code1所示的程序,這個程序展示了Thrust庫的一些顯著特點。

Code1

#include <iostream>
#include <cstdio>
#include <ctime>
#include <cmath>#include <cuda_runtime.h>
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <thrust/generate.h>
#include <thrust/sort.h>
#include <cstdlib>int main()
{thrust::host_vector<int> h_vec(1 << 24);thrust::device_vector<int> d_vec = h_vec;thrust::generate(h_vec.begin(), h_vec.end(), rand);thrust::sort(d_vec.begin(), d_vec.end());thrust::copy(d_vec.begin(), d_vec.end(), h_vec.begin());return 0;
}

Code1分配了兩個向量容器:host_vector與 device_vector。host_vector位于主機端,device_vector位于GPU設備端。Thrust 的向量容器與C++ STL中的向量容器類似,host_vector與 device_vector 是通用的容器(即可以存儲任何數據類型),可以動態調整大小。如Code1所示,容器可以自動分配和釋放內存空間并且簡化主機端和設備端之間的數據交換。

程序在向量容器上執行時,使用了generate、sort和copy算法。采用了STL中的迭代器進行遍歷。在這個例子中,迭代器h_vec.beginO和h_vec.end()分別指向容器的第一個元素和最后一個元素的后一個位置(與STL一致左閉右開)。通過計算h_vec.end() – h_vec.beginO,我們可以得到容器的大小。

注意,在執行排序算法的時候,Thrust 會建議啟動一個或多個CUDA kernel,但編程人員并不需要進行相關配置,因為Thrust的接口已經將這些細節抽象化了。對于性能敏感變量(比如 Thrust 庫的網格和塊大小)的選擇,內存管理的細節,甚至排序算法的選擇都留給具體實現的人自行決定。

迭代器和內存空間

雖然向量迭代器類似于數組的指針,但它們還包含了一些額外的信息。注意,我們不需要指定在 device_vector 元素上操作的sort算法,也不用暗示復制操作是從設備內存端到主機內存端。在Thrust庫中,每個范圍的內存空間可以通過迭代器參數自動推斷,并調度合適的算法進行執行。

另外,關于內存空間,Thrust 的迭代器對大量信息進行隱式編碼,這些信息可以用來指導進程調度。比如,Code1中sort的例子,它對基本的整型數據類型進行比較操作。在這個例子中,Thrust庫中采用高度優化的基數排序(radix sort)算法,要比基于數據之間比較的排序算法(例如歸并排序算法速度快很多。需要注意的是,這個調度過程并不會造成性能或存儲開銷:迭代器對元數據編碼只存在于編譯階段并且它的調度策略已經確定。實際上,Thrust的靜態調度策略可以利用迭代器類型的任何信息。

互操作性

Thrust庫完全由CUDA C/C++實現,并且保持了與CUDA 生態系統其余部分的互操作性。互操作性是一個重要特性,因為沒有一個單一的語言或庫能夠很好地解決所有問題。例如,盡管Thrust 算法在內部使用了像共享存儲器的CUDA特性,但是并沒有為用戶提供機制通過 Thrust庫直接使用共享存儲器。因此,有時候應用程序需要直接訪問CUDAC,實現一些特定的算法。Thrust和CUDA C之間的互操作性允許程序員只修改少量外圍代碼,就能用CUDA kerel函數替換Thrust kerel函數,反之亦然。

將Thrust轉換成CUDA C很簡單,類似于用標準C代碼使用C++STL。外部庫通過從向量中抽取“原始”指針,可以訪問駐留在Thrust容器中的數據。Code2中的代碼示例說明了使用原始指針轉換,得到指向device_vector內容的整型指針。

Code2

//Thrust 與 CUDA C/C++的互操作//Thrust dev To CUDA kernel
thrust::device_vector<int> d_vec(1 << 24);thrust::device_vector<int> dev_Y;reduction1<int> << <gridDim, threads, threads.x * sizeof(double) >> > (thrust::raw_pointer_cast(d_vec.data()),temp,thrust::raw_pointer_cast(dev_Y.data()));//CUDA dev To Thrust devint* h_test = (int*)malloc((1 << 24) * sizeof(int));int* d_test;cudaMemcpy(d_test, h_test, (1 << 24) * sizeof(int),cudaMemcpyHostToDevice)thrust::device_ptr<int> dev_ptr = thrust::device_pointer_cast(d_test);thrust::sort(dev_ptr, dev_ptr + (1 << 24));

在Code2中,函數raw_pointer_cast()接受設備向量d_vec的元素0的地址(.data()與STL類似)作為參數,并且返回原始C指針raw_ptr。這個指針可用于調用CUDA C API函數(如cudaMemset()函數),或者作為參數傳遞到CUDA C kerel函數中(reduction1函數)。

將 Thrust 算法應用到原始C指針也很簡單。一旦原始指針經過 device_ptr 的包裝,它便能作為普通的 Thrust迭代器。

Code2中,C指針raw_ptr 指向設備內存中由函數cudaMalloc()分配的一片內存。通過 device_pointer_cast()函數,它可以轉換為指向設備向量的設備指針。轉換后的指針提供了一些內存空間信息,以便Thrust庫調用適當的算法實現,并且為從主機端訪問設備存儲器提供了方便的機制。在這個例子中,這些信息指明dev_ptr指向設備內存中的向量并且元素類型是整型。

Thrust的原生CUDA C的互操作性保證Thrust總是能作為CUDA C的很好補充,Thrust和CUDA C的結合使用通常比單獨使用CUDA C或者Thrust效果好。事實上,即使能夠完全使用 Thrust 函數編寫完整的并行程序,但是在某些特定領域內直接使用CUDA C實現函數功能會取得更好的結果。原生CUDA C的抽象層次允許程序員能夠細粒度地控制計算資源到特定問題的精確映射。在這個層次上編程給開發者提供了實現特定算法的靈活性。互操作性也有利于迭代開發策略:(1)使用Thrust庫快速開發出并行應用的原型:(2)確定程序熱點;(3)使用CUDA C實現特定算法并作必要優化。

Thrust性能分析

Code

耗時測試代碼

#include <iostream>
#include <cstdio>
#include <ctime>
#include <cmath>#include <cuda_runtime.h>
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <thrust/generate.h>
#include <thrust/sort.h>
#include <cstdlib>#include "helper_cuda.h"
#include "error.cuh"using namespace std;const int FORTIME = 50;template<typename T> __global__
void reduction1(T* X, uint32_t n, T* Y) {extern __shared__ uint8_t shared_mem[];T* partial_sum = reinterpret_cast<T*>(shared_mem);uint32_t tx = threadIdx.x;uint32_t i = blockIdx.x * blockDim.x + threadIdx.x;partial_sum[tx] = i < n ? X[i] : 0;__syncthreads();for (uint32_t stride = 1; stride < blockDim.x; stride <<= 1) {if (tx % (2 * stride) == 0)partial_sum[tx] += tx + stride < n ? partial_sum[tx + stride] : 0;__syncthreads();}if (tx == 0) Y[blockIdx.x] = partial_sum[0];
}template<typename T>
void rand_array(T* array, size_t len) {for (int i = 0; i < len; ++i) {array[i] = ((T)rand()) / RAND_MAX;}
}int main(int argc, char* argv[])
{thrust::host_vector<int> h_vec(1 << 24);cout <<"Test Mem :\t" << (1 << 24) * sizeof(int) / 1024 / 1024 << "MB" << endl;thrust::host_vector<int> h_vec1(5);thrust::generate(h_vec1.begin(), h_vec1.end(), rand);h_vec1[0] = 0;h_vec1[4] = 4;cout << "h_vec1[4] = \t" << h_vec1[4] << endl << "h_vec1.end() - 1 = \t" << *(h_vec1.end() - 1) << endl;thrust::generate(h_vec.begin(), h_vec.end(), rand);thrust::device_vector<int> d_vec(1 << 24);cudaEvent_t start, stop;float elapsed_time;checkCudaErrors(cudaEventCreate(&start));checkCudaErrors(cudaEventCreate(&stop));checkCudaErrors(cudaEventRecord(start));for (int i = 0; i < FORTIME; i++)d_vec = h_vec;checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start, stop));std::cout << "thrust HostToDevice elapsed_time:" << elapsed_time / FORTIME << std::endl;thrust::sort(d_vec.begin(), d_vec.end());checkCudaErrors(cudaEventRecord(start));for (int i = 0; i < FORTIME; i++)thrust::copy(d_vec.begin(), d_vec.end(), h_vec.begin());checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start, stop));std::cout << "thrust Copy DeviceToHost elapsed_time:" << elapsed_time / FORTIME << std::endl;checkCudaErrors(cudaEventRecord(start));for (int i = 0; i < FORTIME; i++)h_vec = d_vec;checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start, stop));std::cout << "thrust DeviceToHost elapsed_time:" << elapsed_time / FORTIME << std::endl;//-------------------------------------------------------int* h_test = (int*)malloc((1 << 24) * sizeof(int));int* d_test;if (h_test == nullptr)return -1;rand_array(h_test, 1 << 24);checkCudaErrors(cudaMalloc((void**)&d_test, (1 << 24) * sizeof(int) ));checkCudaErrors(cudaEventRecord(start));for (int i = 0; i < FORTIME; i++)checkCudaErrors(cudaMemcpy(d_test, h_test, (1 << 24) * sizeof(int),cudaMemcpyHostToDevice));checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start, stop));std::cout << "cudaMemcpy HostToDevice elapsed_time:" << elapsed_time / FORTIME << std::endl;checkCudaErrors(cudaEventRecord(start));for (int i = 0; i < FORTIME; i++)checkCudaErrors(cudaMemcpy(h_test, d_test, (1 << 24) * sizeof(int), cudaMemcpyDeviceToHost));checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start, stop));std::cout << "cudaMemcpy DeviceToHost elapsed_time:" << elapsed_time / FORTIME << std::endl;//Thrust 與 CUDA C/C++的互操作thrust::device_ptr<int> dev_ptr = thrust::device_pointer_cast(d_test);thrust::sort(dev_ptr, dev_ptr + (1 << 24));thrust::device_vector<int> dev_Y;dim3 threads(1024);dim3 gridDim;uint32_t temp = 1 << 24; int sumTime = 0;do {gridDim = dim3((temp + threads.x - 1) / threads.x);d_vec = dev_Y;dev_Y.resize(gridDim.x);checkCudaErrors(cudaEventRecord(start));reduction1<int> << <gridDim, threads, threads.x * sizeof(double) >> > (thrust::raw_pointer_cast(d_vec.data()),temp,thrust::raw_pointer_cast(dev_Y.data()));checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start, stop));sumTime += elapsed_time;temp = gridDim.x;} while (temp > 1);free(h_test);cudaFree(d_test);return 0;
}

具體代碼參考Code

可見Thrust的HostToDev、DevToHost和copy()耗時與CUDA C相似。


Reduction函數耗時分析:

Thrust雖然方便但是相對于固定優化的CUDA C耗時更長。其它Reduction函數請參考:【CUDA】 歸約 Reduction

參考文獻:

1、大規模并行處理器編程實戰(第2版)

2、???CUDA C 編程:基礎與實踐

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

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

相關文章

【linux】 sudo apt update報錯——‘由于沒有公鑰,無法驗證下列簽名: NO_PUBKEY 3B4FE6ACC0B21F32’

【linux】 sudo apt update報錯——‘由于沒有公鑰&#xff0c;無法驗證下列簽名&#xff1a; NO_PUBKEY 3B4FE6ACC0B21F32’ 在運行sudo apt update時遇到報錯&#xff0c;由于沒有公鑰&#xff0c;無法驗證下列簽名&#xff1a; NO_PUBKEY 3B4FE6ACC0B21F32 解決方法&#x…

C++八股(五)之Linux常用命令

目錄 一、Linux常用命令有哪些? 二、Linux中查看進程運行狀態的指令、tar解壓文件的參數。??? 三、如何創建一個新的目錄??? 四、說說如何以root權限運行某個程序。? 五、linux里如何查看一個想知道的進程?? 六、Linux里如何查看帶有關鍵字的日志文件?? 七、…

Qt:11.輸入類控件(QLineEdit-單行文本輸入控件、QTextEdit-多行文本輸入控件、QComboBox-下拉列表的控件)

一、QLineEdit-單行文本輸入控件&#xff1a; 1.1QLineEdit介紹&#xff1a; QLineEdit 是 Qt 庫中的一個單行文本輸入控件&#xff0c;不能換行。允許用戶輸入和編輯單行文本。 1.2屬性介紹&#xff1a; inputMask 設置輸入掩碼&#xff0c;以限定輸入格式。setInputMask(con…

react學習——25redux實現求和案例(完整版)

1、目錄結構 2、count/index.js import React, {Component} from "react"; //引入store,用于獲取數據 import store from ../../redux/store //引入actionCreator 專門創建action對象 import {createDecrementAction,createIncrementAction} from ../../redux/coun…

CSS【詳解】邊框 border,邊框-圓角 border-radius,邊框-填充 border-image,輪廓 outline

邊框 border border 是以下三種邊框樣式的簡寫&#xff1a; border-width 邊框寬度 —— 數值 px&#xff08;像素&#xff09;,thin&#xff08;細&#xff09;,medium&#xff08;中等&#xff09;,thick&#xff08;粗&#xff09;border-style 邊框線型 —— none【默認值…

78. UE5 RPG 創建技能數據并初始化技能ui

在上一篇文章里&#xff0c;我們創建了技能的UI&#xff0c;接下來&#xff0c;我們要考慮如何實現對技能UI的填充&#xff0c;肯定不能直接寫死&#xff0c;需要有一些方法去實現技能的更新。我們期望能夠創建一個技能數據&#xff0c;然后根據數據通過回調的方式實現數據的更…

GET正常,POST獲取不到數據

環境復現 前臺&#xff1a; wx.request({url: xxxxxx,method: POST,header: {"content-type": "application/json"},success(res) {console.log(res);},fail(err) {console.error(網絡請求失敗, err);}}); 后端使用springboot&#xff1a; RequestMappin…

一鍵掌握天氣動態 - 基于Vue和高德API的實時天氣查詢

前言 本文將學習如何使用Vue.js快速搭建天氣預報界面,了解如何調用高德地圖API獲取所需的天氣數據,并掌握如何將兩者有機結合,實現一個功能豐富、體驗出色的天氣預報應用 無論您是前端新手還是有一定經驗,相信這篇教程都能為您帶來收獲。讓我們一起開始這段精彩的Vue.js 高德…

桌面懸浮備忘錄哪個好?能在桌面懸浮使用的備忘app

備忘錄是我們日常工作和生活中的常用工具&#xff0c;它幫助我們記錄重要信息&#xff0c;提醒我們完成各項任務。而將備忘錄懸浮在桌面上使用&#xff0c;無疑能進一步提高我們的工作效率。想象一下&#xff0c;在處理復雜的工作任務時&#xff0c;你能夠隨時在桌面上查看提醒…

C++原創娛樂系列抽搐的井號

玩法&#xff1a; 一次性輸入大量w&#xff0c;s&#xff0c;a&#xff0c;d&#xff0c;然后即可欣賞抽搐的井號 上代碼 #include"bits/stdc.h" #include"Windows.h" using namespace std; int main(){int w10,a10;char n;while(1){for(int i0;i<w;…

JS獲取本機ip地址方法

前端獲取本機ip地址&#xff1b;使用第三方免費API <script>function ipJson(ipJson) {console.log(獲取到的網絡IP,ipJson);//可以把結果存在window上&#xff0c;方便調用window.ipJson ipJson;} </script> <script src"https://whois.pconline.com.cn/…

產品使用手冊深度剖析:五步快速敲定產品手冊策劃思路

引言 在這個信息爆炸的時代&#xff0c;產品使用手冊不僅是產品的“說明書”&#xff0c;更是品牌與用戶之間建立情感連接的橋梁。一份優秀的手冊&#xff0c;能夠迅速吸引用戶的注意力&#xff0c;引導他們輕松上手&#xff0c;并深入體驗產品的魅力。那么&#xff0c;如何撰…

ruoyi項目swagger文檔升級knife4j文檔

注釋admin模塊中的swagger依賴加入knife4j依賴 <!-- swagger3--> <!-- <dependency>--> <!-- <groupId>io.springfox</groupId>--> <!-- <artifactId>springfox-boot-starter</artifactId>--…

IDEA常用技巧薈萃:精通開發利器的藝術

1 概述 在現代軟件開發的快節奏環境中&#xff0c;掌握一款高效且功能全面的集成開發環境&#xff08;IDE&#xff09;是提升個人和團隊生產力的關鍵。IntelliJ IDEA&#xff0c;作為Java開發者的首選工具之一&#xff0c;不僅提供了豐富的編碼輔助功能&#xff0c;還擁有高度…

flowable框架 6.8 自定義函數方法

為了比對流程中條件的checkbox&#xff0c;由于本身elui的checkbox是亂序的&#xff0c;所以需要這個自定義函數來判斷 環境&#xff1a;jdk1.8 flowable6.8 springboot2 1.自定義函數 import org.springframework.stereotype.Component;import java.util.Arrays; import ja…

預算有限?如何挑選經濟適用的安全管理系統?

如今&#xff0c;無論是信息安全、生產安全還是人員安全&#xff0c;都直接關系到企業的穩定運營和長遠發展。然而&#xff0c;對于許多中小企業而言&#xff0c;高昂的安全管理系統投入往往成為一大難題。那么&#xff0c;在預算有限的情況下&#xff0c;如何挑選一款既經濟適…

Github 2024-07-07php開源項目日報 Top9

根據Github Trendings的統計,今日(2024-07-07統計)共有9個項目上榜。根據開發語言中項目的數量,匯總情況如下: 開發語言項目數量PHP項目9Blade項目2JavaScript項目1Laravel:表達力和優雅的 Web 應用程序框架 創建周期:4631 天開發語言:PHP, BladeStar數量:75969 個Fork數…

如何整合生成的人工智能?(GenAI)為你未來的工作增加動力

生成人工智能(GenAI)它發展迅速&#xff0c;以前所未有的速度取得了突破。人工智能將繼續改變各行各業&#xff0c;預計2023年至2030年的年增長率將達到37.3%。由于一種新的知識工作者現在面臨被取代的風險&#xff0c;生成式人工智能的驚人崛起進一步加劇了這種緊迫性。據《未…

如何快速學好一門新技術

目錄 學習步驟 1. 基本了解 2. 快速上手 3. 系統學習 4. 主動運用 5. 了解原理 6. 深入源碼 7. 修改源碼 8. 推陳出新 學到哪一步? 無論學習任何編程技術,都可以遵循以下步驟: 學習步驟 1. 基本了解 首先了解這項技術的用途、優缺點及適用場景。建議通過網上的經…

Apache Hadoop文件上傳、下載、分布式計算案例初體驗

上篇&#xff1a;Apache Hadoop完全分布式集群搭建無坑指南-CSDN博客 通過上篇&#xff0c;我們搭建了完整的Hadoop集群&#xff0c;此篇我們簡單通過集群上傳和下載文件&#xff0c;同時測試分布式worldCount案例。后續的篇章再對分布式計算、分布式存儲作更深的理解。 上傳…